如何得到一个Promise对象
- new Promise()
- 类方法 Promise.xxx(): .resolve() .reject() .all() .race()
- 原型方法 Promise.prototype.xxx(): .then() .catch() .finally()
特性
- Promise 对象的错误具有“冒泡”特性,会一直向后传递,直到被捕获为止;这就是为什么 .catch() 和 .then()的第二个参数方法 的作用一致。
- Promise 内部的错误不会影响到 Promise 外部的代码,俗称“Promise 会吃掉错误”。所以建议使用 .catch 而不是 .then 的第二个参数来捕获 reject 行为。
基本用法
const p = new Promise((resolve, reject) => {
if(/*success*/) {
resolve(/* success result */)
} else {
reject(/* fail reason */)
}
})
/**
* 以下为注释版
*/
// p将是一个promise实例,在new时传入的resolve、reject是形参,从后文中取得
// Promise将在声明时立即执行,没法被打断,所以当运行这行代码的时候,上文环境中就多一个叫p的promise实例,值是对应状态的实例
const p = new Promise((resolve, reject) => {
// 一般promise用于执行异步操作
// 当异步操作成功时,执行从后文传入的resolve位置方法,并传值, 传值可以是任意类型
// 如果后文没有传入resolve方法,不会报错,也不会有任何反应
if(/*success*/) {
resolve(/* success result */)
} else {
// 当异步操作失败时,执行从后文传入的reject位置方法,并传值,传值可以是任意类型
// 如果后文没有传入reject方法,会报错 Uncaught(in promise)
reject(/* fail reason */)
}
})
实例方法
then()
- then 的第一个参数即为 resolve 状态下的回调函数,也即是上文注释中「从后文传入的resolve位置方法」;第二个参数是 reject状态下的回调函数
- then 方法返回一个新的Promise实例(而不是原来的那个Promise实例!),因此 then 支持链式写法,可以 p.then().then()
- 不同于第一个 then 的参数是从 promise 中拿取的,第二个及后面的参数来自于前一个 then 的
return
- 如果前一个 then 中 return 的依旧是一个 promise,那么后一个 then 会等待这个promise执行完,如以下示例代码
const p = new Promise((resolve, reject) => {
resolve('1')
}).then((res) => {
console.log('第一个then');
return new Promise(resolve => {
setTimeout(() => {
resolve('222')
}, 1000)
})
}).then(res => {
// 会间隔1s才打印
console.log('第二个then');
console.log(res);
})
catch()
- catch() 用于捕获之前的 Promise(不一定是上一个,因为错误会被冒泡),和 then 的第二个参数不同的是,他也可以捕获 resolve 回调参数中的运行时语法错误。
- catch() 同样返回一个新的Promise实例,所以它也支持链式写法,并且
如果之前的 Promise 没有错误,那么会跳过传入的 catch方法 ,直接执行后续的.then(),如下代码: ```javascript Promise.resolve() .catch(function(error) { console.log(‘oh no’, error); }) .then(function() { console.log(‘carry on’); });
// carry on
<a name="2181dc73"></a>
### finally()
- 不管 Promise 对象最后状态如何都会执行的操作
```javascript
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
finally
传入的回调方法不接受任何参数,这意味着没有办法知道 前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。finally
同样返回一个 Promise 实例,也即是可以继续向下 then 或者 catch,但是很奇怪的是, .finally().then(res) 的 then 获取不到finally return的值,如下代码:
const p1 = new Promise(resolve => {
setTimeout(() => { resolve('111') }, 1000)
}).then(res => {
console.log(res)
}).finally(res => {
console.log(res);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ffffff')
}, 1000)
})
}).then(res => {
console.log(res); // undefined ???
})
但是如果把resolve改为reject(‘ffffff’),catch(res) 是可以拿到值的,就很迷惑
类方法
all()
- 用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
- 如果数组中的单项有不为 Promise 实例的,会调用 Promise.resolve() 将其转换为 Promise实例 再继续处理。
- 如果数组中的每个 Promise 实例的状态都变为 fulfilled ,或者其中有一个变为 rejected ,
p
的状态才会变化。 - 如果结束时,数组中的每个 Promise 实例的状态都是 fulfilled ,那么拿到的值会是
- 注意,当给数组中的Promise实例添加 .then 或者 .catch 的回调之后,all() 就无法获取到对应的回调了,这是因为这个变量已经指向 .then 或者 .catch 对应的Promise实例了,如以下示例:
const p1 = new Promise(resolve => {
setTimeout(() => {
resolve('111')
}, 1000)
}).then(res => {
console.log(res)
return res + 'p1'
})
const p2 = new Promise(resolve => resolve('222')).then(res => {
console.log(res)
return res + 'p2'
})
Promise.all([p1, p2]).then(res => {
console.log(res) // 输出会是 [ '111p1', '222p2' ]
})
race()
- 用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
- 如果数组中的单项有不为 Promise 实例的,会调用 Promise.resolve() 将其转换为 Promise实例 再继续处理。
- 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数
resolve()
- 将现有对象转为 Promise 对象,返回一个 Promise实例,等价于:
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
- Promise.resolve() 接收四种参数:
- 一个 Promise 实例:原样返回。
- 一个具有 then 方法的对象:将对象转为 Promise对象,立即执行对象中的 then 方法。
- 不是具有then方法的对象,或根本就不是对象:返回一个新的 Promise 对象,状态为 resolved ,会将传入 Promise.resolve 这个值作为参数传给返回Promise 对象的回调函数。
- 不带有任何参数:直接返回一个resolved状态的 Promise 对象。
reject()
- 除了 resolve 状态变成 reject 状态,和下面提到的参数区别,其余部分与 resolve 一样。
- Promise.reject() 方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与 Promise.resolve 方法不一致,resolve 是调用 then 中的方法。
const thenable = {
then(resolve, reject) {
reject('出错了');
}
};
Promise.reject(thenable).catch(e => {
console.log(e === thenable)
})
// true
ES2020 : allSettled()
- 用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.allSettled([p1, p2, p3]);
- 只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 ES2020 引入。
- 该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,Promise 的监听函数接收到的参数是一个数组,每个成员对应一个传入Promise.allSettled()的 Promise 实例。
const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
console.log(results);
// [{status: 'fulfilled', value: 42}, {status: 'rejected', reason: -1}]
});