Promise与async/await
Promise与async/await
Promise
与async/await
在于解决回调地狱而出现,他们两者有点不同,执行async
函数返回的是Promise
对象,而async
相当于Promise
的then
。
Promise
使用 JavaScript 编写代码会大量的依赖异步计算,计算那些我们现在不需要但将来某时候可能需要的值。所以 ES6 引入了一个新的概念,用于更简单地处理异步任务:Promise
。
Promise
对象是对我们现在尚未得到但将来会得到值的占位符;它是对我们最终能够得知异步计算结果的一种保证。如果我们兑现了我们的承诺,那结果会得到一个值。如果发生了问题,结果则是一个错误,一个为什么不能交付的借口。使用Promise
的一个最佳例子是从服务器获取数据:我们要承诺最终会拿到数据,但其实总有可能发生错误。
一个Promise
必然处于以下几种状态之一:
- 待定(pending) :初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled) :意味着操作成功完成。
- 已拒绝(rejected) :意味着操作失败。
下面是Promise
的流程图:
Promise的用法
下面是Promise
的基础用法,setTimeout
模拟的是异步请求,因为setTimeout
里调用了resolve
,此时的Promise
状态为resolved
,请求成功之后执行then
里面的内容。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolve');
});
});
p1.then((res) => {
console.log('p1 res:' + res);
console.log(p1);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolve');
});
}).then((res) => {
console.log('p2 res:' + res);
console.log(p2);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolve');
reject('reject');
});
}).then((res) => {
console.log('p3 res:' + res);
console.log(p3);
});
const p4 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolve');
reject('reject');
});
}).then((res) => {
console.log('p4 res:' + res);
console.log(p4);
}).catch((err) => {
console.log('p4 err:' + err);
console.log(p4);
});
以上是执行了resolve()
的Promise
,他们都去执行了回调函数then()
。接下来我们看看reject()
的例子
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('reject');
resolve('resolve');
});
}).then((res) => {
console.log('p1 res:' + res);
console.log(p1);
}).catch((err) => {
console.log('p1 err:' + err);
console.log(p1);
});
const p2 = new Promise((resolve, reject) => {
throw('error');
}).then((res) => {
console.log('p2 res:' + res);
console.log(p2);
}).catch((err) => {
console.log('p2 err:' + err);
console.log(p2);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
throw('error');
});
}).then((res) => {
console.log('p3 res:' + res);
console.log(p3);
}).catch((err) => {
console.log('p3 err:' + err);
console.log(p3);
});
setTimeout(() => {
console.log(p3);
});
通过执行代码发现,p1
p2
被执行了catch
回调,原因就是Promise
主动执行了reject()
或者在同步代码里面抛出了异常。但是我们发现p3
没有执行catch
回调,并且打印出来的Promise p3
状态为pending
,说明Promise
的异步代码里面抛出的异常不会被catch
到。
接下来我们再来看看catch
调用的调用:
const p1 = new Promise((resolve, reject) => {
throw('error');
}).then((res) => {
console.log('p1 res:' + res);
console.log(p1);
}).catch((err) => {
console.log('p1 err:' + err);
console.log(p1);
return 100;
}).then((res) => {
console.log('p1 res1:' + res);
console.log(p1);
});
const p2 = new Promise((resolve, reject) => {
throw('error');
}).then((res) => {
console.log('p2 res:' + res);
console.log(p2);
}).catch((err) => {
console.log('p2 err:' + err);
console.log(p2);
throw('error');
}).then((res) => {
console.log('p2 res1:' + res);
console.log(p2);
}).catch((err) => {
console.log('p2 err1:' + err);
console.log(p2);
});
这里有个小细节,就是在执行catch
回调中,没有抛出异常,这时候返回的是一个状态是fulfilled
的Promise
,他会继续执行之后的then
回调。
const p1 = new Promise(function(resolve, reject) {
resolve();
console.log('p1 resolve');
throw('error');
console.log('p1 error');
});
const p2 = new Promise(function(resolve, reject) {
reject();
console.log('p2 reject');
throw('error');
console.log('p2 error');
});
最后是抛出异常,在resolve()
或reject()
后面抛出的错误会被忽略,但是其他在抛出错误代码之上的代码还是会被执行。
下面是Promise
的总结:
-
执行了
resolve()
,Promise状态会变成fulfilled
,即 已完成状态 -
执行了
reject()
,Promise状态会变成rejected
,即 被拒绝状态 -
Promise只以
第一次为准
,第一次成功就永久
为fulfilled
,第一次失败就永远状态为rejected
-
Promise的同步代码中有
throw
的话,就相当于执行了reject()
-
Promise里没有执行
resolve()
、reject()
以及throw
的话,这个promise的状态也是pending
-
基于上一条,
pending
状态下的promise不会执行回调函数then()
-
执行
catch
回调中,没有抛出异常,这时候返回的是一个状态是fulfilled
的Promise
-
Promise的异步代码中有
throw
的话,Promise无法捕获,只能通过用try...catch
包裹Promise解决 -
基于上一条,
pending
状态下的promise不会执行回调函数then()
-
在
resolve()
或reject()
后面抛出的错误会被忽略
async/await
async 函数是使用async
关键字声明的函数。async 函数是AsyncFunction
构造函数的实例,并且其中允许使用await
关键字。async
和await
关键字让我们可以用一种更简洁的方式写出基于Promise
的异步行为,而无需刻意地链式调用Promise
。
下面是async
的基础用法:
async function async1 () {
console.log('async1 000');
let await1 = await 1000;
console.log('await1:' + await1);
console. log('async1 100');
let await2 = await Promise.resolve(2000);
console.log('await2:' + await2);
console. log('async1 200');
let await3 = await p1;
console.log('await3:' + await3);
console. log('async1 300');
let await4 = await async2();
console.log('await4:' + await4);
console. log('async1 400');
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolve');
});
});
async function async2() {
console. log('async2 000');
return 400;
}
async1();
可以看到上面async1函数
里的代码以同步的形式编写,但是具有异步行为。
如果await
的异步代码出现异常则可以使用try/catch
代码块捕获:
async function async2 () {
try {
let await1 = await p2;
} catch (err){
console.log('catch async2'); //catch async2
}
}
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('resolve');
});
});
async2();
-
async
函数一定会返回一个Promise
对象 -
如果一个
async
函数的返回值看起来不是Promise
,那么它将会被隐式地包装在一个Promise
中 -
async
函数可能包含0
个或者多个await
表达式 -
await
表达式会暂停整个async
函数的执行进程并出让其控制权,只有当其等待的基于Promise
的异步操作被兑现或被拒绝之后才会恢复进程 -
Promise
的解决值会被当作该await
表达式的返回值 -
使用
async/await
关键字就可以在异步代码中使用普通的try/catch
代码块
以一道面试题结束
const async1 = async () => {
console.log('async1');
setTimeout(() => {
console.log('timer1');
}, 2000);
await new Promise(resolve => {
console.log('promise1');
})
console.log('async1 end');
return 'async1 success';
}
console.log('script start');
async1().then(res => console.log(res));
console.log('script end');
Promise.resolve(1)
.then(res => {
console.log(res);
return 5
})
.then(Promise.resolve(3))
.catch(4)
.then(res => console.log(res))
setTimeout(() => {
console.log('timer2');
}, 1000);
点击查看答案
'script start' 'async1' 'promise1' 'script end' 1 5 'timer2' 'timer1'
解析:
-
第一步代码开始,先声明了一个函数
async
,还未执行,接着继续执行代码,遇到了script start
,将其输出到控制台 -
执行
async1
,程序跳去async
里面,遇到了async1
,将其输出到控制台 -
接着是
setTimeout
,这时候开启一个计时器,2秒后会将setTimeout
的回调放去宏任务执行 -
遇到关键字
await
,await
的是一个Promise
,直接执行Promise
,将promise1
输出到控制台 -
接下来要留意下,代码里的
Promise
是没有resolve()
或reject()
的,就是说await
还一直在等待Promise
-
到这一步,
async1
里面的代码执行完了,接着执行剩下的代码,于是遇到了script end
,将其输出到控制台 -
然后又遇到一个
Promise.resolve(1)
,接着把.then()
放去微任务里面等待执行 -
接下来要回到
setTimeout
,1秒后会将setTimeout
的回调放去宏任务执行 -
这时候主线程的代码已经执行完了,就开始取出微任务里面的任务执行,即执行
.then()
,而.then()
所在的Promise
已经resolve
,并且返回了1
,所以.then
的res
是1
,将其输出到控制台 -
同理继续执行
Promise.resolve(3)
所在的.then
,又因为这个then
没有return
,所以将其上级的参数透传下去,故最后执行的.then
的res
是5
,将其输出到控制台
最后,让我们一起加油吧!
参考资料:
转载自:https://juejin.cn/post/7213285858249195579