解决回调地域的promise详解+async/await
一、Promise 对象
描述:Promise一个异步编程的构造函数,比传统的回调函数更加强大,避免了层层嵌套的回调函数。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
特点:
- 状态不受外界影响。 三种状态:
pending(进行中)、fulfilled(已成功)和rejected(已失败)。 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。状态只能是pending=>fulfilled or pending=>rejected
Promise对象提供统一的接口,使得控制异步操作更加容易。
基本用法:
1 function timeout(ms) { 2 return new Promise((resolve, reject) => { 3 setTimeout(resolve, ms, 'done'); 4 }); 5 } 6 7 timeout(100).then((value) => { 8 console.log(value); 9 });
调用resolve和reject时有参数,参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数参数除了正常的值,还可能是另一个 Promise 实例,比如像下面这样。
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
})
上面代码中,一个异步操作的结果是返回另一个异步操作。注意,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。
const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) const p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error)) // Error: fail
上面代码中,p1是一个 Promise,3 秒之后变为rejected。p2的状态在 1 秒之后改变,resolve方法返回的是p1。
由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。
所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。
调用resolve或reject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。所以,推荐在它们前面加上return语句,这样就不会有意外。
1 new Promise((resolve, reject) => { 2 return resolve(1); 3 // 后面的语句不会执行 4 console.log(2); 5 })
Promise原型上的then方法和catch方法
作用:为 Promise 实例添加状态改变时的回调函数。两个参数值都是可选回调函数,第一个成功回调,参数resolved的值,第二个错误回调,参数为错误的reason。
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。
const someAsyncThing = function () { return new Promise(function (resolve, reject) { // 下面一行会报错,因为x没有声明 resolve(x + 2); }); }; someAsyncThing().then(function () { console.log('everything is great'); }).catch(res => { console.log(res + 123); }); setTimeout(() => { console.log(123) }, 2000); // Uncaught (in promise) ReferenceError: x is not defined // 123
上面代码 由于捕获了错误,会打印出错误提示ReferenceError: x is not defined,但是不会退出进程、终止脚本执行。
Promise 内部的错误不会影响到 Promise 外部的代码,即使回调函数里面有错误,通俗的说法就是“Promise 会吃掉错误”。
为此在nodejs中有个unhandleRejection事件专门监听promsie未捕获的reject错误。
process.on('unhandledRejection', function (err, p) { throw err; });
上面代码中,unhandledRejection事件的监听函数有两个参数,第一个是错误对象,第二个是报错的 Promise 实例,它可以用来了解发生错误的环境信息。
1 const promise = new Promise(function (resolve, reject) { 2 resolve('ok'); 3 setTimeout(function () { throw new Error('test') }, 0) 4 }); 5 promise.then(function (value) {}); 6 // ok 7 // Uncaught Error: test

上面代码中,Promise 指定在下一轮“事件循环”再抛出错误。到了定时器执行时候,promise已经执行结束,所以这个错误实在函数体外抛出的,会冒泡到外层???成了未捕获的错误
const someAsyncThing = function() { return new Promise(function(resolve, reject) { // 下面一行会报错,因为x没有声明 resolve(x + 2); }); }; someAsyncThing() .catch(function(error) { console.log('oh no', error); }) .then(function() { console.log('carry on'); }); // oh no [ReferenceError: x is not defined] // carry on

Promise原型上的finally方法
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
Promise.all()
将多个Promise的实例包装为一个新的Promise实例,接受promise数组。如:
const p = Promise.all([p1, p2, p3]);
p的状态由p1、p2、p3决定,分成两种情况。
(1)只有状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。(都成功才成功)
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数(一个失败就失败)。
Promise.race()
race英文意思比赛,语法和all一样。拿到最新改变状态那个promise的返回值。
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。
Promise.any()
和race差不多,但是他是所有都变为reject才改变为reject,resolve的话拿到最先成功那个值
Promise.any([
fetch('https://v8.dev/').then(() => 'home'),
fetch('https://v8.dev/blog').then(() => 'blog'),
fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => { // 只要有一个 fetch() 请求成功
console.log(first);
}).catch((error) => { // 所有三个 fetch() 全部请求失败
console.log(error);
});
Promise.resolve()
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
二、async函数
描述:Generator函数的语法糖。一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。
async函数对 Generator 函数的改进如下:
- 内置执行器。Generator 函数的执行必须靠执行器,所以才有了
co模块(co模块需要去学习一下),而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。 - 更好的语义。
async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值会自动转成立即 resolved 的 Promise 对象)。 - 更广的适用性。
- 返回值是 Promise。Generator 函数的返回值是 Iterator,你可以用
then方法指定下一步的操作。
返回 Promise 对象
async函数返回一个 Promise 对象。
async函数内部return语句返回的值,会成为then方法回调函数的参数。
async function f() { return 'hello world'; } f().then(v => console.log(v)) // "hello world"

浙公网安备 33010602011771号