分析Promise
实现有利于理解异步函数调用过程和链式调用的实现.
最简单的Promise
实现有7个主要属性, state
(状态), value
(成功返回值), reason
(错误信息), resolve
方法, reject
方法, then
方法.
简单代码实现
其中state
有三个状态分别为: pending
, fulfilled
和rejected
.
一般情况下我们是这样使用Promise
的: new Promise((resolve, reject) => {})
.
我们使用new
操作符生成Promise
实例, 显然Promise
是一个类.
在生成Promise
实例时, 我们会把带有resolve
和reject
两个参数的方法作为参数传给Promise
, 因此Promise
的构造函数初始化时, 有一个方法参数并在初始化时调用, 我们暂时把这个方法参数命名为executor
.
首先我们让state
的初始状态设为pending
.
resolve
和reject
都是方法, 当执行resolve()
, 并且state
状态为pending
时, state
状态变成fulfilled
表示已成功. 这时, 把resolve
的参数赋给value
成功返回值.
而当执行reject()
, 并且状态为pending
时, state
状态则变成rejected
. 同时, 把reject
的参数赋给reason
错误信息.
then
也有两个方法参数, 分别代表成功回调onFulfilled
和失败回调onRejected
.
例如:
p.then(
(data) =>{
console.log('Success:', data);
},
error => {
console.log('Fail:', error);
}
);
当state
状态是fulfilled
的时候, 调用onFulfilled()
方法把成功返回值value
作为参数.
当state
状态是rejected
的时候, 调用onRejected()
方法把成功返回值reason
作为参数.
按照上面思路, 很容易得出下面代码:
class Promise{
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
}
};
try {
// 立即执行函数
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
let x = onFulfilled(this.value);
};
if (this.state === 'rejected') {
let x = onRejected(this.reason);
};
}
}
复杂代码实现:
但是then
是被Promise
多次调用的. 先过一遍复杂情况的代码:
class Promise{
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onResolvedCallbacks.forEach(fn=>fn());
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try {
// 立即执行函数
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
// onFulfilled如果不是函数,就忽略onFulfilled,变成默认直接返回value的函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected如果不是函数,就忽略onRejected,变成默认直接扔出错误的函数
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
// 声明返回的promise2
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
let x = onFulfilled(this.value);
// resolvePromise 在后面有详细讲解怎么实现
resolvePromise(promise2, x, resolve, reject);
})
this.onRejectedCallbacks.push(() => {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
})
};
if (this.state === 'fulfilled') {
let x = onFulfilled(this.value);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
};
if (this.state === 'rejected') {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
};
});
// 返回promise,完成链式
return promise2;
}
catch(fn){
return this.then(null,fn);
}
}
下面结合上面代码分析下复杂代码的实现.
复杂实现的改进:
then
多次调用
为什么需要onResolvedCallbacks
和onRejectedCallbacks
存储then
方法的队列?
在不是链式调用的情况下, then
方法可以多次被调用, 例如:
let p = new Promise((s, r) => {
s('resoved.');
})
p.then(()=>{
console.log('Run then');
})
p.then(()=>{
console.log('2nd run then');
})
因此需要两个数组分别储存任务队列.
resolve
方法改造后的流程:
state
(状态)在pending
的情况下, 把state
变成fulfiled
(防止在其他状态下改变, 实现只能改变一次状态);- 把
value
变量存储起来, 供以后then
调用. - 储在
onResolvedCallbacks
数组(队列)里的方法都运行一遍.
reject
方法改造后的流程:
state
(状态)在pending
的情况下, 把state
变成rejected
(防止在其他状态下改变, 实现只能改变一次状态);- 把
reason
变量存储起来, 供以后then
调用. - 把存储在
onRejectedCallbacks
数组(队列)里的方法都运行一遍.
then
链式调用
then
不仅可以多次调用, 也可以链式调用, 例如:
p
.then(()=>{console.log('Success.')})
.then(()=>{console.log('and then.')})
因此then
需要返回一个新的Promise
实例, 并继续调用新实例的then
方法形成链式.
理解起来的代码是这样的:
(p.then()).then()
那么下面我们在then
里套娃, 创建新Promise
实例, 并返回这个新的Promise
实例, 如果还有下个then
, 那就以此类推继续套娃, 目标都是为了执行外面传入的onFulfilled
或者onRejected
方法.
executor
里的逻辑:
如果state
是pending
,
- 把
onFulfilled
方法(即实例then
的处理成功方法)放到onResolvedCallbacks
数组队列里. - 把
onRejected
方法(即实例then
的处理失败方法)放到onRejectedCallbacks
数组队列里.
如果state
是fulfilled
, 则代入value
立即执行onFulfilled
方法.
如果state
是rejected
, 则代入value
立即执行onRejected
方法.
如果在onFulfilled
和onRejected
方法里返回了新的Promise
实例需要继续往下执行, 那么onFulfilled
和onRejected
的返回结果放到resolvePromise();
判断并处理.
既然是用套娃的手段实现的, 就有可能把自己套进自己陷入死循环, 例如这样:
var p2 = p.then(data => {
return p2;
})
我们把处理新Promise
实例回调的部分分离成一个方法resolvePromise
.
分析resolvePromise
resolvePromise 代码实现
function resolvePromise(promise2, x, resolve, reject){
// 循环引用报错
if(x === promise2){
// reject报错
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 防止多次调用
let called;
// x不是null 且x是对象或者函数
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
// A+规定,声明then = x的then方法
let then = x.then;
// 如果then是函数,就默认是promise了
if (typeof then === 'function') {
// 就让then执行 第一个参数是this 后面是成功的回调 和 失败的回调
then.call(x, y => {
// 成功和失败只能调用一个
if (called) return;
called = true;
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject);
}, err => {
// 成功和失败只能调用一个
if (called) return;
called = true;
reject(err);// 失败了就失败了
})
} else {
resolve(x); // 直接成功即可
}
} catch (e) {
// 也属于失败
if (called) return;
called = true;
// 取then出错了那就不要在继续执行了
reject(e);
}
} else {
resolve(x);
}
}
resolvePromise 代码分析
让p = new Promise()
, p.then(rs, rj)
返回了一个新的Promise
实例(假设为p2
), 可以让p2.then(rs2, rj2)
继续执行...
rs
运行后得到的结果为x
(一般没有返回值, 所以为undefined
), x
代入resolvePromise()
方法检验.
- 如果
x
是普通的值, 立刻执行resolve
方法处理队列. - 如果
x
有属性then
并且then
是函数, 那个可以被认为x
是Promise
, 这种情况针对的是then
里嵌套并返回新的Promise
实例. - 解决自己套自己的死循环,
then
里嵌套返回自身,then
继续call
自身然后返回自身, 进入死循环. 为了避免这种情况, 必须在resolvePromise
判断是否是自身, 解决循环引用问题.
实例运行分析
const test = function() {
return new Promise((res, rej)=>{
setTimeout(()=>{
console.log('ok');
res();
}, 2000)
})
};
test().then(()=>{console.log(`1st then`)}).then(()=>{console.log(`2nd then`)});
- 当定时器结束时, 输出'ok', 执行了第一个
Promise
实例的resolve
, 第一个Promise
实例的state
状态变成fulfilled
; - 第一个
Promise
实例的onResolvedCallbacks
队列执行任务,- 完成第一个
then
的onFulfilled
方法, 并把返回的结果放到resolvePromise
里执行, 这时的resolve
和reject
函数是第二个Promise
实例的(即then
里的Promise
实例);
let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject);
resolvePromise
方法如果遇到普通值则执行第二个Promise
实例的resolve
方法, 将第二个Promise
的状态从pending
转为fulfilled
, 然后执行第二个Promise
实例的onResolvedCallbacks
队列.- 第二个
Promise
实例存在调用then
, 因此它的onResolvedCallbacks
队列里存放着第三个Promise
的resolve
和reject
方法并等待着第二个Promise
实例的fulfilled
状态改变(即resolve
被调用). - 因为没有第三个
then
, 因此第三个Promise
实例的onResolvedCallbacks
队列没有任务, 调用链到此为止.
- 完成第一个
其他Promise静态方法
//resolve方法
Promise.resolve = function(val){
return new Promise((resolve,reject)=>{
resolve(val)
});
}
//reject方法
Promise.reject = function(val){
return new Promise((resolve,reject)=>{
reject(val)
});
}
//race方法
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject)
};
})
}
//all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)
Promise.all = function(promises){
let arr = [];
let i = 0;
function processData(index,data){
arr[index] = data;
i++;
if(i == promises.length){
resolve(arr);
};
};
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(data=>{
processData(i,data);
},reject);
};
});
}