异步处理的演化
JS实现异步的代码模型主要依托于回调
dom.addEventListener("click", function(e){
// 回调函数作为第二个参数传递,函数可接收一个参数
}, {})
dom.onclick = function(e){
// 回调函数作为属性传递,函数可接收一个参数
}
fs.readFile("./txt", function(err, buffer){
// 回调函数作为最后一个参数传递,函数可接收两个参数
})
这种设计实际上是有缺陷的
-
没有统一的标准
-
容易陷入回调地狱(callback hell)
/* 异步任务:依次发送7次网络请求,拿到服务器数据 */ asyncConnect("地址1", (resp1) => { // to do something asyncConnect("地址2", (resp2) => { // to do something asyncConnect("地址3", (resp3) => { // to do something asyncConnect("地址4", (resp4) => { // to do something asyncConnect("地址5", (resp5) => { // to do something asyncConnect("地址6", (resp6) => { // to do something asyncConnect("地址7", (resp7) => { // to do something }); }); }); }); }); }); });
后来,JS社区提出了Promise A+
规范,希望把异步规范化,并消除回调地狱
再后来,ES6 官方标准中提出了 Promise API
来处理异步,它满足Promise A+
规范
由于异步处理变得标准了,就给ES官方提供了进一步改进的空间,于是在ES7中出现了新的语法async await
,它更加完美的解决了异步处理问题
Promise的概念
一个promise就是一个对象,它表示一个异步任务
异步任务内部保存了它的进展状态,规范中约定有三种状态,不同的状态属于不同的阶段
状态的转换
任务开始时,始终处于未决阶段的挂起状态
任务在未决阶段的时候,有能力将其推向已决。比如,当从服务器拿到数据后,我们就从未决阶段推向已决的resolved状态,如果网络不好,导致出错了,我们就从未决阶段推向已决的rejected状态
我们把从未决推向已决的resolved状态的过程,叫做resolve,从未决推向已决的rejected状态的过程,叫做reject
这种状态和阶段的变化是不可逆的,也就是说,一旦推向了已决,就无法重新改变状态
任务完成后附带的数据
任务从未决到已决时,可能附带一些数据,比如:跑步完成后的用时、网络请求后从服务器拿到的数据
任务的后续处理
任务已决后(有了结果),可能需要进一步做后续处理
针对resolved的后续处理,称之为thenable,针对rejected的后续处理,称之为catchable
Promise的基本使用
ES6 提供了一套API来适配上面提到的异步模型,这个API即Promise
var pro = new Promise((resolve, reject)=>{
//未决阶段的代码,这些代码将立即同步执行,表示任务启动后要做的事情
//...
//在合适的时候,将任务推向已决
//resolve(数据):将任务推向resovled状态,并附加一些数据
//reject(数据):将任务推向rejected状态,并附加一些数据
})
注意
- 任务一旦进入已决后,所有企图改变任务状态的代码都将失效
- 以下代码可以让任务到达rejected状态
- 调用reject
- 代码执行报错
- 抛出错误
拿到Promise对象后,可以通过then方法指定后续处理
pro.then(thenable, catchable)
//或
pro.then(thenable)
pro.catch(catchable)
无论是thenable还是catchable,均是下面格式的函数
function (data){
//data为状态数据
}
注意:后续处理函数一定是异步函数,并且放到微队列中
面试题
-
下面代码输出什么?
const promise = new Promise((resolve, reject) => { console.log(1) resolve("a"); resolve("b"); reject("c") console.log(2) }) promise.then(data => { console.log(data) }, resean => { console.log(reason) }) console.log(4)
-
下面的代码输出什么?
setTimeout(() => { console.log(1); }); var pro = new Promise((resolve, reject) => { console.log(2); resolve(3); console.log(4); throw 5; console.log(6); }); console.log(7); pro.then( (data) => { console.log(data); }, (reason) => { console.log(reason); } );
更多知识
Promise是可以链式调用的
var pro1 = ...; // pro1 是一个异步任务,它完成后会得到一个数字3
pro1
.then(n=>{
console.log(n); // 输出:3
return n * 2;
})
.then(n=>{
console.log(n); // 输出:6
return n * 2;
})
.then(n=>{
console.log(n); // 输出:12
})
/*
异步任务:依次发送7次网络请求,拿到服务器数据
*/
asyncConnect("地址1", (resp1) => {
// to do something
asyncConnect("地址2", (resp2) => {
// to do something
asyncConnect("地址3", (resp3) => {
// to do something
asyncConnect("地址4", (resp4) => {
// to do something
asyncConnect("地址5", (resp5) => {
// to do something
asyncConnect("地址6", (resp6) => {
// to do something
asyncConnect("地址7", (resp7) => {
// to do something
});
});
});
});
});
});
});
/*
异步任务:依次发送7次网络请求,拿到服务器数据
*/
asyncConnect("地址1")
.then((resp) => {
// to do something
return asyncConnect("地址2");
})
.then((resp) => {
// to do something
return asyncConnect("地址3");
})
.then((resp) => {
// to do something
return asyncConnect("地址4");
})
.then((resp) => {
// to do something
return asyncConnect("地址5");
})
.then((resp) => {
// to do something
return asyncConnect("地址6");
})
.then((resp) => {
// to do something
return asyncConnect("地址7");
}).then((resp) => {
// to do something
});
如果使用ES7
的async
和await
,代码会更加优雅
/*
异步任务:依次发送7次网络请求,拿到服务器数据
*/
async function doRequest(){
var resp1 = await asyncConnect("地址1");
// to do something
var resp2 = await asyncConnect("地址2");
// to do something
var resp3 = await asyncConnect("地址3");
// to do something
var resp4 = await asyncConnect("地址4");
// to do something
var resp5 = await asyncConnect("地址5");
// to do something
var resp6 = await asyncConnect("地址6");
// to do something
var resp7 = await asyncConnect("地址7");
// to do something
}
面试题
-
下面的代码输出什么?
var a; var b = new Promise((resolve, reject) => { console.log('promise1'); resolve(); }).then(() => { console.log('promise2'); }).then(() => { console.log('promise3'); }).then(() => { console.log('promise4'); }); a = new Promise(async (resolve, reject) => { console.log(a); await b; console.log(a); console.log('after1'); await a resolve(true); console.log('after2'); }); console.log('end');
-
下面的代码输出什么?
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2'); } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0) async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end');