一 、Promise
(一)Promise三种状态
Promise是ES6中提供的一个异步编程的解决方案,Promise本身是一个构造函数。
console.log(typeof Promise) // function
在JavaScript中,所有的代码都是同步执行的,而在js在进行网络操作,浏览器实践,任务队列时,就导致很多代码必须要进行回调,出现回调地狱。
ajax就是典型的异步操作。
Promise则是异步编程的一种解决方案,它有两个特点:
(1)对象的状态不受外界影响
(2)状态是不可逆不可改变
resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的
错误,作为参数传递出去。
Promise一共有三种状态,分别是:
- pending [待定] 初始状态
- fulfilled [实现] 操作成功
- rejected [被否决] 操作失败
初始状态为pending,如果成功则resolved,如果失败测reject。
当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;
promise状态一经改变,不会再变。
Promise对象的状态改变,只有两种可能:
从pending变为Resolved
从pending变为rejected。
Promise 的基本用法:
let promise = new Promise((resolve, reject) => {
resolve()
}).then(res => {console.log(res)})
Pomise构造函数resolve()和reject()接收的函数作为参数,该函数有两个参数分别为resolve和reject,调用resolve则会代表成功,调用reject则会代表无效。
class Promise {
constructor(executor) {
this.status = "pending"; // 默认状态
this.value; // resolve 成功时的值
this.error; // reject 失败时的值
let resolve = res => {
if(this.status === "pending") {
this.value = res;
this.status = "resolved";
}
}
let reject = err => {
if(this.status === "pending") {
this.error = err;
this.status = "rejected";
}
}
executor(resolve, reject);
}
}
1、pending [待定] 初始状态
测试一下,如果不去resolve,也不去reject
// 测试一下:
new Promise((resolve, reject) => {
})
那么Promise应该是初始状态。我们将上面的代码执行测试一下,得到结果如下:
此时状态是:{status: “pending”}。
2、fulfilled [实现] 操作成功
当我们执行 resolve
// 测试一下:
new Promise((resolve, reject) => {
resolve('成功啦~');
})
此时状态是:{status: “resolve”}。
3、rejected [被否决] 操作失败
当执行reject
// 测试一下:
new Promise((resolve, reject) => {
resolve('失败啦~')
})
此时状态是:{status: “reject”}。
这里举一个类比的例子来加深对Promise的理解:
有一女朋友对男朋友许下承诺(new Promise)要怀孕,生个胖嘟嘟的孩子,对于该承诺是否兑现就有两种情况:
(1)resolve实现承诺,成功换上了孩子,进入then;
(2)reject没有实现承诺,在约定时间内没有成功怀孕,进入catch,返回错误信息 ;
不过不管有没有怀孕成功,最后,他们两个都会成功结婚。
代码如下:
const isPregnant = false;
const promise = new Promise((resolve, reject) => {
if (isPregnant) {
resolve('孩子他爹');
} else {
reject('老公');
}
});
Promise
.then(name => {
console.log(`男人成为了${name}!`);
})
.catch(name => {
console.log(`男人成为了${name}!`);
})
.finally(() => {
console.log(`男人和女人最终结婚了!`);
});
(二)、基于Promise处理多次Ajax请求(链式调用)【重要】
1、ajax多次链式调用
实际开发中,我们经常需要同时请求多个接口。比如说:在请求完接口1的数据data1之后,需要根据data1的数据,继续请求接口2,获取data2;然后根据data2的数据,继续请求接口3。
这种场景其实就是接口的多层嵌套调用。有了 promise之后,我们可以把多层嵌套调用按照线性的方式进行书写,非常优雅。
也就是说:Promise 可以把原本的多层嵌套调用改进为链式调用。
代码举例:(多次 Ajax请求,链式调用)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise</title>
</head>
<body>
<script type="text/javascript">
function queryData(url) {
var promise = new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) return;
if (xhr.readyState == 4 && xhr.status == 200) {
resolve(xhr.responseText);
} else {
reject('接口请求失败');
}
};
xhr.responseType = 'json';
xhr.open('get', url);
xhr.send(null);
});
return promise;
}
queryData('http://localhost:3000/api1')
.then(
data1 => {
console.log(JSON.stringify(data1));
return queryData('http://localhost:3000/api2');
},
error1 => {
console.log(error1);
}
)
.then(
data2 => {
console.log(JSON.stringify(data2));
return queryData('http://localhost:3000/api3');
},
error2 => {
console.log(error2);
}
)
.then(
data3 => {
console.log(JSON.stringify(data3));
},
error3 => {
console.log(error3);
}
);
</script>
</body>
</html>
2、 return 的函数返回值
return 后面的返回值,有两种情况:
情况1:返回 Promise 实例对象。返回的该实例对象会调用下一个 then。
情况2:返回普通值。返回的普通值会直接传递给下一个then,通过 then 参数中函数的参数接收该值。
3、Promise 的常用API:实例方法【重要】
Promise 自带的API提供了如下实例方法:
promise.then():获取异步任务的正常结果。
promise.catch():获取异步任务的异常结果。
promise.finaly():异步任务无论成功与否,都会执行。
代码如下:
写法一:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
function queryData() {
return new Promise((resolve, reject) => {
setTimeout(function() {
var data = { retCode: 0, msg: 'qianguyihao' }; // 接口返回的数据
if (data.retCode == 0) {
// 接口请求成功时调用
resolve(data);
} else {
// 接口请求失败时调用
reject({ retCode: -1, msg: 'network error' });
}
}, 100);
});
}
queryData()
.then(data => {
// 从 resolve 获取正常结果
console.log('接口请求成功时,走这里');
console.log(data);
})
.catch(data => {
// 从 reject 获取异常结果
console.log('接口请求失败时,走这里');
console.log(data);
})
.finally(() => {
console.log('无论接口请求成功与否,都会走这里');
});
</script>
</body>
</html>
写法二:(和上面的写法1等价)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
function queryData() {
return new Promise((resolve, reject) => {
setTimeout(function() {
var data = { retCode: 0, msg: 'qianguyihao' }; // 接口返回的数据
if (data.retCode == 0) {
// 接口请求成功时调用
resolve(data);
} else {
// 接口请求失败时调用
reject({ retCode: -1, msg: 'network error' });
}
}, 100);
});
}
queryData()
.then(
data => {
// 从 resolve 获取正常结果
console.log('接口请求成功时,走这里');
console.log(data);
},
data => {
// 从 reject 获取异常结果
console.log('接口请求失败时,走这里');
console.log(data);
}
)
.finally(() => {
console.log('无论接口请求成功与否,都会走这里');
});
</script>
</body>
</html>
注意:写法1和写法2的作用是完全等价的。只不过,写法2是把 catch 里面的代码作为 then里面的第二个参数而已。
4、Promise 的常用API:对象方法【重要】
Promise 自带的API提供了如下对象方法:
- Promise.all():并发处理多个异步任务,所有任务都执行成功,才能得到结果。
- Promise.race(): 并发处理多个异步任务,只要有一个任务执行成功,就能得到结果。
下面来详细介绍:
Promise.all()代码举例:
代码举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="text/javascript">
/*
封装 Promise 接口调用
*/
function queryData(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) return;
if (xhr.readyState == 4 && xhr.status == 200) {
// 处理正常结果
resolve(xhr.responseText);
} else {
// 处理异常结果
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
}
var promise1 = queryData('http://localhost:3000/a1');
var promise2 = queryData('http://localhost:3000/a2');
var promise3 = queryData('http://localhost:3000/a3');
Promise.all([promise1, promise2, promise3]).then(result => {
console.log(result);
});
</script>
</body>
</html>
Promise.race()代码举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="text/javascript">
/*
封装 Promise 接口调用
*/
function queryData(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) return;
if (xhr.readyState == 4 && xhr.status == 200) {
// 处理正常结果
resolve(xhr.responseText);
} else {
// 处理异常结果
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
}
var promise1 = queryData('http://localhost:3000/a1');
var promise2 = queryData('http://localhost:3000/a2');
var promise3 = queryData('http://localhost:3000/a3');
Promise.race([promise1, promise2, promise3]).then(result => {
console.log(result);
});
</script>
</body>
</html>