[TOC]
阮老师的promise是不是很难看完,看这个吧,我给精简总结下
1. 什么是Promise
简单说是一个容器,里面保存着未来才会结束的事件(通常是异步操作)的结果
-
特点
- 状态不受外界影响,(
pending
进行中,fulfilled
已成功 ,rejected
已失败 )。只有异步操作的结果决定当前是哪一状态。下文用resolved
表示fulfilled
- 状态一旦改变,就不会再改变。从
pending
变为fulfilled
和从pending
变为rejected
,这时称resolved
已定型
通过Promise,可将异步操作变为同步操作的流程表达出来,避免回调嵌套地狱,使得异步操作更加容易。
但是:
-
- Promise一新建就会立即执行,无法中途取消。
-
- 当处于
pending
状态时,无法得知当前是刚开始还是即将完成。
- 当处于
-
- 内部抛出的错误,如果不设置回调函数,不会反映到外部。
- 状态不受外界影响,(
2. 基本使用
1. new Promise()
构造方法接收一个函数,该函数由js引擎提供的两个函数参数: resolve
和 reject
resolve
: 成功,状态pending
=>resolved
,并将异步操作结果作为参数传递出去。reject
:失败, 状态pending
=>rejected
, 并将异步操作报出的错误,作为参数传递出去。
const promise = new Promise(function(resolve, reject) {
//...some code
if (/* 异步操作成功 */) {
resolve(value);
} else {
reject(error);
}
});
2. Promise.resolve()
将现有对象转为Promise对象。
const ajaxPromise = Promise.resolve($.ajax('./whatever.json'));
等价写法:
Promise.resolve('foo');
// <=>
new Promise(resolve => resolve('foo'))
其参数情况:
-
参数是
Promise
实例: 什么都不做,直接返回。 -
参数是
thenable
对象(指具有then
方法的对象):将这个对象转为Promise
对象,然后立即执行thenable
对象的then
方法 -
参数不是具有
then
方法的对象, 或根本就不是对象:返回一个状态为resolved
新的Promise
对象,该参数会同时传给回调函数const p = Promise.resolve("mouse"); p.then(str => {console.log(str)});// "mouse"
-
不传: 直接返回一个
resolved
状态的Promise
对象。
注: resolve()
的Promise对象,是在本轮 event loop结束时执行
setTimeout(()=>{console.log(1)}, 0);//下一轮 event loop开始时执行。
Promise.resolve().then(()=>{console.log(2)});
console.log(3);
//3
//2
//1
3. Promise.reject()
也会返回一个新的
Promise
实例,但其状态为rejected
。
参数说明:
和 Promise.resolve()
不同,它的参数会不做修改的作为reject
的理由,变成后续方法的参数。
const thenalble = {
then(resolve, reject) {reject('some error')}
};
Promise.reject(thenable).catch(e=>{
console.log(e === thenable);// true
});
4. Promise.prototype.then()
实例方法, Promise状态改变时的回调函数。有两个参数,对应改变的两个状态
resolved
状态的回调函数。rejected
状态的回调函数。then方法返回一个新的Promise实例,因此可以可以链式调用
5. Promise.prototype.catch()
用于指定发生错误时候的回调函数,其实就是
rejected
时的回调,所以等同于:
.then(null|undefinded, rejection)
一搬写在最后,捕获前面链式调用出现的错误。
注:
-
状态如果已经变成
resolved
, 再抛出错误是无效的,因为状态一改变,就永久改变了。所有才叫 ”Promise“。 -
Promise对象的错误具有 冒泡 性质,会一直向后传递,直到被捕获为止。
-
一搬来说,不在
then
里面使用第二个参数,而总是使用catch
。使用catch更接近同步的写法,且可catch
前面所有执行中的错误。 -
如果没有使用
catch
,Promise对象抛出的错误不会传递到外层代码,外层代码依旧正常执行。”Promise会吃掉错误“。 -
其返回值还是一个
Promise
对象,所有还是可以继续then
6. Promise.prototype.finally()
看到这个总能想起,java里面:不管怎么样,数据库用完了总是要关闭的。
finally:不管怎么样,最后都要执行的操作。与状态无关。本质上就是then
的特例
promise.finally(()=>{
// some code
});
//等同于
promise.then((result)=>{
// some code
return result;
}, (error)=>{
// some code
throw error;
});
finally()
方法帮忙给简写了。
finally()
方法实现:
Promoise.prototype.finally = function(callbakc) {
let p = this.constructor;
return this.then(
value => P.resolve(callback()).then(()=>value),
reason => P.resolve(callback()).then(()=>{throw reason})
);
}
//finally.总是会返回原来的值。
7. Promise.all()
将多个Promise实例,包装成一个新的Promise实例
const p = Promise.all([p1, p2, p3]);
//参数是Promise实例的数组,如果数组成员不是promise,
//会先调用Promise.resolve()。参数也可以不是数组,但必须具有Iterator接口。
p的状态由p1, p2,p3决定:
p1, p2, p3
的状态都变成resolved
,则p的状态变成resolved
。此时 p1, p2, p3的返回值组成一个数组,传递给p
的回调函数。p1, p2, p3
其中一个被rejected
,则p
的状态为rejected
。此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
8. Promise.race()
同样是将多个Promise实例,包装成一个新的Promise实例。
const p = Promise.race([p1, p2, p3]);
//参数同 Promise.all();
只要 p1, p2, p3
,之中有一个实例率先改变状态。p
的状态就跟着改变。率先改变的Promise实例的返回值,就传递给p
的回调函数。
一个典型的应用就设置请求超时
const p = Promise.race([
fetch('/remoteData'),
new Promise((resolve, reject)=>{
setTimeout(()=>{reject(new Error('request timeout')},5000);
})
]);
p.then(console.log).catch(console.error);
9. Promise.allSettled()
由ES2020引入
同样是多个Promise实例,包装成一个新的Promise实例。
只有等所有参数实例都返回结果,包装实例才会结束。
Promise.all()
,无法确定所有请求都结束。
10. Promise.any()
目前还在 提案
同样多个Promise实例,包装成一个Promise实例。 只要参数实例中,有一个
resolved
了,包装实例状态就会变成resolved
。
与
Promse.race()
相像。不过Promise.any()
不会因为某个Promise变成rejected
状态而结束。
11. Promise.try()
想用
Promise
,又不用区分是同步还是异步函数,比如: 不管函数f
是否包含异步操作,都用then
方法指定下一步流程。
一般做法:
Promise.resolve().then(f);
//此时如果f是同步函数,则其会在本轮event loop的末尾执行。eg:
const f = () => console.log(1);
Promise.resolve().then(f);
console.log(2);
//2
//1
try
的作用就是让同步函数同步执行,异步函数异步执行。
const f = () => console.log(1)
Promise.try(f);
console.log(2);
//1
//2
注: Promise
的库:Bluebird , Q, when
都提供的这个方法。
鉴于目前还是提案,使用的话可以用shim:
npm i es6-promise-try
import promiseTry from "es6-promise-try"
阮老师给出的另外两种方法:
//第一种
const f = () => {console.log(1)};
(async () => f())().then().catch();//async()=> f()会吃掉 f()抛出的异常,需要Promise.catch()
console.log(2);
//第二种
const f = ()=>console.log(1);
(()=>{new Promise(resolve=>resolve(f()))})();
console.log(2);