YvetteLau/Blog

Promise的源码实现(完美符合Promise/A+规范)

YvetteLau opened this issue · 30 comments

Promise是前端面试中的高频问题,我作为面试官的时候,问Promise的概率超过90%,据我所知,大多数公司,都会问一些关于Promise的问题。如果你能根据PromiseA+的规范,写出符合规范的源码,那么我想,对于面试中的Promise相关的问题,都能够给出比较完美的答案。

我的建议是,对照规范多写几次实现,也许第一遍的时候,是改了多次,才能通过测试,那么需要反复的写,我已经将Promise的源码实现写了不下七遍。

Promise的源码实现

/**
 * 1. new Promise时,需要传递一个 executor 执行器,执行器立刻执行
 * 2. executor 接受两个参数,分别是 resolve 和 reject
 * 3. promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled
 * 4. promise 的状态一旦确认,就不会再改变
 * 5. promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, 
 *      和 promise 失败的回调 onRejected
 * 6. 如果调用 then 时,promise已经成功,则执行 onFulfilled,并将promise的值作为参数传递进去。
 *      如果promise已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。
 *      如果promise的状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)
 * 7. then 的参数 onFulfilled 和 onRejected 可以缺省
 * 8. promise 可以then多次,promise 的then 方法返回一个 promise
 * 9. 如果 then 返回的是一个结果,那么就会把这个结果作为参数,传递给下一个then的成功的回调(onFulfilled)
 * 10. 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调(onRejected)
 * 11.如果 then 返回的是一个promise,那么需要等这个promise,那么会等这个promise执行完,promise如果成功,
 *   就走下一个then的成功,如果失败,就走下一个then的失败
 */

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function Promise(executor) {
    let self = this;
    self.status = PENDING;
    self.onFulfilled = [];//成功的回调
    self.onRejected = []; //失败的回调
    //PromiseA+ 2.1
    function resolve(value) {
        if (self.status === PENDING) {
            self.status = FULFILLED;
            self.value = value;
            self.onFulfilled.forEach(fn => fn());//PromiseA+ 2.2.6.1
        }
    }

    function reject(reason) {
        if (self.status === PENDING) {
            self.status = REJECTED;
            self.reason = reason;
            self.onRejected.forEach(fn => fn());//PromiseA+ 2.2.6.2
        }
    }

    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
    //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
    let self = this;
    //PromiseA+ 2.2.7
    let promise2 = new Promise((resolve, reject) => {
        if (self.status === FULFILLED) {
            //PromiseA+ 2.2.2
            //PromiseA+ 2.2.4 --- setTimeout
            setTimeout(() => {
                try {
                    //PromiseA+ 2.2.7.1
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    //PromiseA+ 2.2.7.2
                    reject(e);
                }
            });
        } else if (self.status === REJECTED) {
            //PromiseA+ 2.2.3
            setTimeout(() => {
                try {
                    let x = onRejected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            });
        } else if (self.status === PENDING) {
            self.onFulfilled.push(() => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            });
            self.onRejected.push(() => {
                setTimeout(() => {
                    try {
                        let x = onRejected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            });
        }
    });
    return promise2;
}

function resolvePromise(promise2, x, resolve, reject) {
    let self = this;
    //PromiseA+ 2.3.1
    if (promise2 === x) {
        reject(new TypeError('Chaining cycle'));
    }
    if (x && typeof x === 'object' || typeof x === 'function') {
        let used; //PromiseA+2.3.3.3.3 只能调用一次
        try {
            let then = x.then;
            if (typeof then === 'function') {
                //PromiseA+2.3.3
                then.call(x, (y) => {
                    //PromiseA+2.3.3.1
                    if (used) return;
                    used = true;
                    resolvePromise(promise2, y, resolve, reject);
                }, (r) => {
                    //PromiseA+2.3.3.2
                    if (used) return;
                    used = true;
                    reject(r);
                });

            }else{
                //PromiseA+2.3.3.4
                if (used) return;
                used = true;
                resolve(x);
            }
        } catch (e) {
            //PromiseA+ 2.3.3.2
            if (used) return;
            used = true;
            reject(e);
        }
    } else {
        //PromiseA+ 2.3.3.4
        resolve(x);
    }
}

module.exports = Promise;

有专门的测试脚本可以测试所编写的代码是否符合PromiseA+的规范。

首先,在promise实现的代码中,增加以下代码:

Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}

安装测试脚本:

npm install -g promises-aplus-tests

如果当前的promise源码的文件名为promise.js

那么在对应的目录执行以下命令:

promises-aplus-tests promise.js

promises-aplus-tests**有872条测试用例。以上代码,可以完美通过所有用例。

对上面的代码实现做一点简要说明(其它一些内容注释中已经写得很清楚):

  1. onFulfilled 和 onFulfilled的调用需要放在setTimeout,因为规范中表示: onFulfilled or onRejected must not be called until the execution context stack contains only platform code。使用setTimeout只是模拟异步,原生Promise并非是这样实现的。

  2. 在 resolvePromise 的函数中,为何需要usedd这个flag,同样是因为规范中明确表示: If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored. 因此我们需要这样的flag来确保只会执行一次。

  3. self.onFulfilled 和 self.onRejected 中存储了成功的回调和失败的回调,根据规范2.6显示,当promise从pending态改变的时候,需要按照顺序去指定then对应的回调。

PromiseA+的规范(翻译版)

PS: 下面是我翻译的规范,供参考

术语

  1. promise 是一个有then方法的对象或者是函数,行为遵循本规范
  2. thenable 是一个有then方法的对象或者是函数
  3. value 是promise状态成功时的值,包括 undefined/thenable或者是 promise
  4. exception 是一个使用throw抛出的异常值
  5. reason 是promise状态失败时的值

要求

2.1 Promise States

Promise 必须处于以下三个状态之一: pending, fulfilled 或者是 rejected

2.1.1 如果promise在pending状态
2.1.1.1 可以变成 fulfilled 或者是 rejected
2.1.2 如果promise在fulfilled状态
2.1.2.1 不会变成其它状态

2.1.2.2 必须有一个value值
2.1.3 如果promise在rejected状态
2.1.3.1 不会变成其它状态

2.1.3.2 必须有一个promise被reject的reason

概括即是:promise的状态只能从pending变成fulfilled,或者从pending变成rejected.promise成功,有成功的value.promise失败的话,有失败的原因

2.2 then方法

promise必须提供一个then方法,来访问最终的结果

promise的then方法接收两个参数

promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled 和 onRejected 都是可选参数
2.2.1.1 onFulfilled 必须是函数类型

2.2.1.2 onRejected 必须是函数类型
2.2.2 如果 onFulfilled 是函数:
2.2.2.1 必须在promise变成 fulfilled 时,调用 onFulfilled,参数是promise的value
2.2.2.2 在promise的状态不是 fulfilled 之前,不能调用
2.2.2.3 onFulfilled 只能被调用一次
2.2.3 如果 onRejected 是函数:
2.2.3.1 必须在promise变成 rejected 时,调用 onRejected,参数是promise的reason
2.2.3.2 在promise的状态不是 rejected 之前,不能调用
2.2.3.3 onRejected 只能被调用一次
2.2.4 onFulfilled 和 onRejected 应该是微任务
2.2.5 onFulfilled 和 onRejected 必须作为函数被调用
2.2.6 then方法可能被多次调用
2.2.6.1 如果promise变成了 fulfilled态,所有的onFulfilled回调都需要按照then的顺序执行
2.2.6.2 如果promise变成了 rejected态,所有的onRejected回调都需要按照then的顺序执行
2.2.7 then必须返回一个promise
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 onFulfilled 或 onRejected 执行的结果为x,调用 resolvePromise
2.2.7.2 如果 onFulfilled 或者 onRejected 执行时抛出异常e,promise2需要被reject
2.2.7.3 如果 onFulfilled 不是一个函数,promise2 以promise1的值fulfilled
2.2.7.4 如果 onRejected 不是一个函数,promise2 以promise1的reason rejected

2.3 resolvePromise

resolvePromise(promise2, x, resolve, reject)

2.3.1 如果 promise2 和 x 相等,那么 reject promise with a TypeError
2.3.2 如果 x 是一个 promsie
2.3.2.1 如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected.
2.3.2.2 如果 x 被 fulfilled, fulfill promise with the same value.
2.3.2.3 如果 x 被 rejected, reject promise with the same reason.
2.3.3 如果 x 是一个 object 或者 是一个 function
2.3.3.1 let then = x.then.
2.3.3.2 如果 x.then 这步出错,那么 reject promise with e as the reason..
2.3.3.3 如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
    2.3.3.3.1 resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
    2.3.3.3.2 rejectPromise 的 入参是 r, reject promise with r.
    2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
    2.3.3.3.4 如果调用then抛出异常e 
        2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略
        2.3.3.3.4.3 否则,reject promise with e as the reason
2.3.3.4 如果 then 不是一个function. fulfill promise with x.
2.3.4 如果 x 不是一个 object 或者 function,fulfill promise with x.

Promise的其他方法

虽然上述的promise源码已经符合PromiseA+的规范,但是原生的Promise还提供了一些其他方法,如:

  1. Promise.resolve()
  2. Promise.reject()
  3. Promise.prototype.catch()
  4. Promise.prototype.finally()
  5. Promise.all()
  6. Promise.race()

下面具体说一下每个方法的实现:

Promise.resolve

Promise.resolve(value) 返回一个以给定值解析后的Promise 对象.

  1. 如果 value 是个 thenable 对象,返回的promise会“跟随”这个thenable的对象,采用它的最终状态
  2. 如果传入的value本身就是promise对象,那么Promise.resolve将不做任何修改、原封不动地返回这个promise对象。
  3. 其他情况,直接返回以该值为成功状态的promise对象。
Promise.resolve = function (param) {
        if (param instanceof Promise) {
        return param;
    }
    return new Promise((resolve, reject) => {
        if (param && param.then && typeof param.then === 'function') {
            setTimeout(() => {
                param.then(resolve, reject);
            });
        } else {
            resolve(param);
        }
    });
}

thenable对象的执行加 setTimeout的原因是根据原生Promise对象执行的结果推断的,如下的测试代码,原生的执行结果为: 20 400 30;为了同样的执行顺序,增加了setTimeout延时。

测试代码:

let p = Promise.resolve(20);
p.then((data) => {
    console.log(data);
});


let p2 = Promise.resolve({
    then: function(resolve, reject) {
        resolve(30);
    }
});

p2.then((data)=> {
    console.log(data)
});

let p3 = Promise.resolve(new Promise((resolve, reject) => {
    resolve(400)
}));
p3.then((data) => {
    console.log(data)
});

Promise.reject

Promise.reject方法和Promise.resolve不同,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。

Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
        reject(reason);
    });
}

Promise.prototype.catch

Promise.prototype.catch 用于指定出错时的回调,是特殊的then方法,catch之后,可以继续 .then

Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected);
}

Promise.prototype.finally

不管成功还是失败,都会走到finally中,并且finally之后,还可以继续then。并且会将值原封不动的传递给后面的then.

Promise.prototype.finally = function (callback) {
    return this.then((value) => {
        return Promise.resolve(callback()).then(() => {
            return value;
        });
    }, (err) => {
        return Promise.resolve(callback()).then(() => {
            throw err;
        });
    });
}

Promise.all

Promise.all(promises) 返回一个promise对象

  1. 如果传入的参数是一个空的可迭代对象,那么此promise对象回调完成(resolve),只有此情况,是同步执行的,其它都是异步返回的。
  2. 如果传入的参数不包含任何 promise,则返回一个异步完成.
  3. promises 中所有的promise都promise都“完成”时或参数中不包含 promise 时回调完成。
  4. 如果参数中有一个promise失败,那么Promise.all返回的promise对象失败
  5. 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let index = 0;
        let result = [];
        if (promises.length === 0) {
            resolve(result);
        } else {
            function processValue(i, data) {
                result[i] = data;
                if (++index === promises.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                    //promises[i] 可能是普通值
                    Promise.resolve(promises[i]).then((data) => {
                    processValue(i, data);
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

测试代码:

var promise1 = new Promise((resolve, reject) => {
    resolve(3);
})
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values); //[3, 42, 'foo']
},(err)=>{
    console.log(err)
});

var p = Promise.all([]); // will be immediately resolved
var p2 = Promise.all([1337, "hi"]); // non-promise values will be ignored, but the evaluation will be done asynchronously
console.log(p);
console.log(p2)
setTimeout(function(){
    console.log('the stack is now empty');
    console.log(p2);
});

Promise.race

Promise.race函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。

如果传的参数数组是空,则返回的 promise 将永远等待。

如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值。

Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        if (promises.length === 0) {
            return;
        } else {
            for (let i = 0; i < promises.length; i++) {
                Promise.resolve(promises[i]).then((data) => {
                    resolve(data);
                    return;
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

测试代码:

Promise.race([
    new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
    undefined,
    new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })
]).then((data) => {
    console.log('success ', data);
}, (err) => {
    console.log('err ',err);
});

Promise.race([
    new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
    new Promise((resolve, reject) => { setTimeout(() => { resolve(200) }, 200) }),
    new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })
]).then((data) => {
    console.log(data);
}, (err) => {
    console.log(err);
});

欢迎关注小姐姐的微信公众号,加入技术交流群。

1

大佬真棒!学习的榜样!

棒棒哒

顶礼膜拜,看多了都是泪😭

小姐姐棒棒的,今天看了你的这篇promise,自己试着写了一遍

小姐姐棒棒的,今天看了你的这篇promise,自己试着写了一遍

看小姐姐的文章能找到好工作

good

let x = new Promise((resolve, reject) => {
  resolve(
    new Promise((resolve, reject) => {
      resolve(2);
    })
  );
}).then(res => {
  console.log(res);
});

我发现一个问题就是,这个在浏览器的Promise中,和这里实现的Promise是不一致的,想问下这是新规范吗

resolvePromise中self是多余的,好几次以为是自身对象,debug走一遍之后发现是window,再往下面看的时候发现并没有用到。

let x = new Promise((resolve, reject) => {
  resolve(
    new Promise((resolve, reject) => {
      resolve(2);
    })
  );
}).then(res => {
  console.log(res);
});

我发现一个问题就是,这个在浏览器的Promise中,和这里实现的Promise是不一致的,想问下这是新规范吗

// 1) Promise 是一个类或者一个方法
// 2) Promise 有三种状态 pending (等待态) fulfilled (成功态) rejected (失败态)
// 默认为pending态
// 只有是pending态的时候才能更改状态为fulfilled 或者 rejected 状态一旦更改就不允许再次被修改
// 3) Promise 内部提供一个成功的value, 一个失败的reason
// 4) Promise 内部提供两个方法 一个resolve 一个reject
// 如果两个方法都不传 默认返回undefined
// resolve或者reject不能抛异常 代码会直接报错
// resolve接收成功的value reject接收失败的reason
// 调用resolve 会把状态从 pending 更改为 fulfilled
// 调用reject 会把状态从 pending 更改为 rejected
// 如果在executor 执行器中异步调用resolve或者reject 例如setTimeout调用
// 如果在异步任务重抛出异常 则会直接执行这个throw抛出的 不会走入到下面的then方法的回调了
// then回调时候状态依然是pending态 需要发布订阅模式处理 将订阅的存放在数组里面
// 为什么存放的是一个数组呢 应为一个实例 可以then多次 一个多次订阅
// 成功的onFulfilledCallBacks 失败的onRejectedCallBacks
// 5) Promise 接收一个 executor 执行器, 默认会立即执行
// 第一个参数就是内部提供的resolve 第二个参数就是内部提供的参数reject
// 多个resolve跟reject嵌套的话 应该以最后一个的作为结果
// 6) Promise 有个then函数 默认有两个参数onFulfilled 和 onRejected
// onFulfilled resolve或者reject调用之后返回的是一个普通值或者一个新的promise调用的resolve的值
// 将value作为参数传递给onFulfilled
//
// onRejected resolve和reject调用之后的回调返回一个promise的reject的值或者抛出异常 将reason作为参数传递给onRejected

// then有返回值

// catch的特点是 如果都没有错误处理 一层层找 没有找到错误处理 会找最近的catch catch也是then

// promise中实现链式调用 返回一个新的promise

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// 判断x的状态 是让promise2 变成 成功态 还是失败
// 此方法为了兼容所有的promise库 尽可能详细 不出错
function resolvePromise(promise2, x, resolve, reject) {
/** PromiseA+ 2.3.2 不能引用同一个对象 会造成死循环*/
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #'));
}
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
// x如果是对象或者函数 说明他有可能是个对象
/** PromiseA+ 2.3.2.2
* 取之可能会报错 then方法可能是由getter定义的
* Object.defineProperty(promise, 'then', {
* get () {
* throw new Error();
* }
* })
* /
let called;
try {
let then = x.then;
if (typeof then === 'function') {
/
* PromiseA+ 2.3.2.2
* let obj = {
* index: 0,
* get then () {
* if (++this.index=== 2) throw error
* }
* }
* obj.then 就会执行
/
// 相当于传进来的值是reject
then.call(
x,
(y) => {
// PromiseA+ 2.3.3.3
// 防止一个promise调用多次 走完成功再走失败 或者走完失败 再走成功 或者一直走成功 或者一直走失败
if (called) return
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
// PromiseA+ 2.3.3.3
if (called) return
called = true;
reject(r);
}
);
} else {
resolve(x);
}
} catch (e) {
// PromiseA+ 2.3.3.3
if (called) return;
called = true;
reject(e);
}
} else {
//这里有问题写的
// 普通值 直接成功就好了
resolve(x);
}
}
function isPromise (x) {
if ((typeof x === 'object' && x != null) || typeof x === 'function') {
try {
let x = x.then
if (typeof x.then === 'function') {
return true
}
return false
} catch (error) {
return false
}
}
return false
}
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallBacks = [];
this.onRejectedCallBacks = [];
let resolve = (value) => {
if (value instanceof Promise) {
// 可能我们初始化的时候传进来的就是个Promise
// 递归解析 直到是普通值为止
// console.log(value)
value.then(resolve, reject);
return false
}
if (this.status === PENDING) {
//PromiseA+ 2.1.1
this.status = FULFILLED;
this.value = value;
this.onFulfilledCallBacks.forEach((fn) => fn());
}
};
let reject = (reason) => {
if (this.status === PENDING) {
//PromiseA+ 2.1.1
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallBacks.forEach((fn) => fn());
}
};
try {
//PromiseA+ 1.1.4
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => v;
onRejected =
typeof onRejected === 'function'
? onRejected
: (err) => {
throw err;
};
// PromiseA+ 2.2.7
// 为了实现链式调用 创建一个新的promise
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
// 执行then中的方法 可能返回的是一个普通值 或者promise执行
// 我要判断x的类型是不是一个promise 如果是promise的话 我要让这个promise执行
// 并采用他的状态 作为promise的成功或者失败
/
* PromiseA+ 2.2.4*/
setTimeout(() => {
/** PromiseA+ 2.2.7.2*/
try {
// 一旦执行then方法报错 就走到外层then的错误处理中 调用promise2的reject方法中
let x = onFulfilled(this.value);
/** PromiseA+ 2.2.7.1*/
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
/** PromiseA+ 2.2.4*/
setTimeout(() => {
/** PromiseA+ 2.2.7.2*/
try {
let x = onRejected(this.reason);
/** PromiseA+ 2.2.7.1*/
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onFulfilledCallBacks.push(() => {
// 切片编程
/** PromiseA+ 2.2.4*/
setTimeout(() => {
/** PromiseA+ 2.2.7.2*/
try {
// 一旦执行then方法报错 就走到外层then的错误处理中 调用promise2的reject方法中
let x = onFulfilled(this.value);
/** PromiseA+ 2.2.7.1*/
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallBacks.push(() => {
/** PromiseA+ 2.2.4*/
setTimeout(() => {
/** PromiseA+ 2.2.7.2*/
try {
// 一旦执行then方法报错 就走到外层then的错误处理中 调用promise2的reject方法中
let x = onRejected(this.reason);
/** PromiseA+ 2.2.7.1*/
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
// 切片编程
});
}
});
return promise2;
}
catch (errCallBack) {
return new Promise((resolve, reject) => {
resolve(this.then(null, errCallBack));
});
}
finally (callback) {
return this.then(
value => new Promise.resolve(callback()).then(() => value),
reason => new Promise.resolve(callback()).then(() => { throw reason })
);
}
}
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let idx = 0;
let arr = []
function processData(y, index){
arr[index] = y
if (arr.length === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
let currentValue = promises[i]
idx++
if (isPromise(currentValue)) {
currentValue.then((y) => {
processData(i)
},reject)
} else {
processData(currentValue, i)
}
}
})
}
Promise.race = function race(entries) {
var Constructor = this; // this 是调用 race 的 Promise 构造器函数。
if (!isArray(entries)) {
return new Constructor(function (_, reject) {
return reject(new TypeError('You must pass an array to race.'));
});
} else {
return new Constructor(function (resolve, reject) {
var length = entries.length;
for (var i = 0; i < length; i++) {
Constructor.resolve(entries[i]).then(resolve, reject);
}
});
}
}
// Promise.resolve 和 Promise.reject 区别
// 传入一个promise的话 resolve
Promise.resolve = function (params) {
return new Promise((resolve, reject) => {
resolve(params)
})
}
Promise.reject = function (params) {
return new Promise((resolve, reject) => {
reject(params)
})
}
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
module.exports = Promise;

看下这个呢

ES6 在 Promise/A+ 规范基础上扩展了一些功能,例如:resolve 解析 promise 和 thenable 对象,根据一些测试用例做了补充和修改(node环境),感兴趣可以参考 https://github.com/lfpdev/promise

非常棒的一篇文章

错的,.then执行应该在同一tick,但是你这不是同一tick

里实现的Promise是不一致的,想问下

浏览器是 native code microtask,这毕竟是模拟的,其实是macrotask。

好文 点赞

个人觉得有两个地方需要改正。
第一个就是 constructor中的 resolve方法需要判断resolve函数的参数是不是当前(实测:只能是当前Promise类,不能与其他类共用)Promsie类的实例,如果是,则调用参数的then方法。实现如下

const resolve = data =>{
// 只能是当前Promise类的实例,其他Pormise类的实例则不可
  if(data instanceof Promise){
    return data.then(resolve, reject)
  }
  if(this.state === PENDING){
    this.state = RESOLVED
    this.value = data
    this.onResolvedCbs.forEach(fn => fn())
  }
}

第二个地方是resolvePromise方法中的如果then不是函数的时候,没有必要再给flag赋值。要弄明白什么时候需要给flag赋值,防止回调被调用多次。当把控制权交出去的时候,需要修改flag。由于无法确定resolve还有reject被调用的次数,所以,需要flag来控制回调只执行一次。当then不是函数的时候,就代表不是promise,就不再需要修改flag了。

2.3.2 x如果是一个Promise,代码中怎么没体现出来?

@YvetteLau 想确认下

关于Promise.resolve的实现是不是对于thenable对象处理缺少了try catch

如果这样的话
Promise.resolve({ then: function() { throw new Error(11111) } }).then(res => console.log(1, res), err => console.log(err, 5555))

会不会有问题

finally 那边是不是可以直接这样写:

Promise.prototype.finally = function (onFinally) {
  return this.then(onFinally, onFinally)
}

finally 那边是不是可以直接这样写:

Promise.prototype.finally = function (onFinally) {
  return this.then(onFinally, onFinally)
}

不行哈,比如你看这样的代码

Promise.resolve(2).finally().then((val) => {
	console.log(val);
})

finally不会影响promise一个状态的传递,你这个代码如果onFinally有返回值,就会影响到后面then的回调

源码复制后报错 @YvetteLau
TypeError: adapter.deferred is not a function at adapter.resolved (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\programmaticRunner.js:13:29) at Object.exports.a fulfilled promise (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\tests\helpers\reasons.js:51:12) at C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\tests\2.2.7.js:58:53 at Array.forEach (<anonymous>) at Suite.<anonymous> (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\tests\2.2.7.js:57:30) at context.describe.context.context (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\node_modules\mocha\lib\interfaces\bdd.js:47:10) at Suite.<anonymous> (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\tests\2.2.7.js:30:5) at context.describe.context.context (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\node_modules\mocha\lib\interfaces\bdd.js:47:10) at Object.<anonymous> (C:\Users\42012\AppData\Roaming\npm\node_modules\promises-aplus-tests\lib\tests\2.2.7.js:15:1) at Module._compile (internal/modules/cjs/loader.js:778:30)

好像对 'thenable' 的参数不适用?

有个问题
this.value如果是个promise对象的话 第一个then拿到的永远是reslove的回调,结果是这个promise对象
实际应该是要根据promise的状态来判断是resolve还是reject.以及是value还是reason.

2.3.2 x如果是一个Promise,代码中怎么没体现出来?

promise 本身就是一种 thenable,下面统一处理了,效果一样的,只是没有按照规范那样实现

resolvePromise中self是多余的,好几次以为是自身对象,debug走一遍之后发现是window,再往下面看的时候发现并没有用到。

确实用不到,resolvePromise 是在全局中声明的函数,this 指向的是全局对象,除非传 promise 实例给 resolvePromise,否则它根本拿不到 promise 实例

``> ```js

let x = new Promise((resolve, reject) => {
resolve(
new Promise((resolve, reject) => {
resolve(2);
})
);
}).then(res => {
console.log(res);
});


我发现一个问题就是,这个在浏览器的Promise中,和这里实现的Promise是不一致的,想问下这是新规范吗

确实,我也发现了这个问题,然后去网上找 Promise 实现,发现很多手写 Promise 实现都没有考虑给 resolve 函数传另一个 promise 对象的情况。

const p1 = new Promise((resolve, _reject) => {
  setTimeout(resolve, 1000, "p1");
});

const p2 = new Promise((resolve, _reject) => {
  resolve(p1);
});

p2.then((res) => {
  console.log(res);
});

对于 es6 的 Promise,输出的是:p1;
而对于楼主手写的 Promise,输出的则是:Promise { status: 'pending', onFulfilled: [], onRejected: [] }
最后在大佬们的回复中找到了解决方案,感谢提供解决方案的大佬们!

里实现的Promise是不一致的,想问下

浏览器是 native code microtask,这毕竟是模拟的,其实是macrotask。

可以使用 window.queueMicroTask(callback) 来队列 microtask

``> ```js

let x = new Promise((resolve, reject) => {
resolve(
new Promise((resolve, reject) => {
resolve(2);
})
);
}).then(res => {
console.log(res);
});


我发现一个问题就是,这个在浏览器的Promise中,和这里实现的Promise是不一致的,想问下这是新规范吗

确实,我也发现了这个问题,然后去网上找 Promise 实现,发现很多手写 Promise 实现都没有考虑给 resolve 函数传另一个 promise 对象的情况。

const p1 = new Promise((resolve, _reject) => {
  setTimeout(resolve, 1000, "p1");
});

const p2 = new Promise((resolve, _reject) => {
  resolve(p1);
});

p2.then((res) => {
  console.log(res);
});

对于 es6 的 Promise,输出的是:p1; 而对于楼主手写的 Promise,输出的则是:Promise { status: 'pending', onFulfilled: [], onRejected: [] }。 最后在大佬们的回复中找到了解决方案,感谢提供解决方案的大佬们!

我也发现了网上的实现都没处理这种这种情况, 只处理了 then 中返回 promise的情况, 如果在 new Promise的时候, 直接 resolve 了另一个 promise, 网上的实现都有问题, 但是能通过 promise a+ 测试