Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
YvetteLau opened this issue Mar 21, 2019 · 30 comments
Open

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

YvetteLau opened this issue Mar 21, 2019 · 30 comments

Comments

@YvetteLau
Copy link
Owner

YvetteLau commented Mar 21, 2019

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

@ljianshu
Copy link

大佬真棒!学习的榜样!

@liuzhilei
Copy link

棒棒哒

@guangyuan-T
Copy link

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

@pengpeng9413
Copy link

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

1 similar comment
@pengpeng9413
Copy link

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

@panzhang5
Copy link

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

@yifan-blog
Copy link

good

@biubiupiu1
Copy link

biubiupiu1 commented Dec 16, 2019

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

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

@c-wchen
Copy link

c-wchen commented Apr 6, 2020

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

@nosillytrying
Copy link

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;

看下这个呢

@lfpdev
Copy link

lfpdev commented Jul 20, 2020

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

@WweiweiSmile
Copy link

非常棒的一篇文章

@Vi-jay
Copy link

Vi-jay commented Sep 22, 2020

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

@zhoutengjiao
Copy link

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

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

@BoBoooooo
Copy link

好文 点赞

@307590317
Copy link

个人觉得有两个地方需要改正。
第一个就是 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了。

@leokang28
Copy link

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

@lixiaohulee
Copy link

@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))

会不会有问题

@KangChangYi
Copy link

KangChangYi commented Feb 2, 2021

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

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

@devinRex
Copy link

devinRex commented Feb 24, 2021

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

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

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

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

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

@Larmyliu
Copy link

Larmyliu commented Mar 5, 2021

源码复制后报错 @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)

@Steve-Hawk
Copy link

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

@chenc916
Copy link

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

@Chorer
Copy link

Chorer commented Jul 29, 2021

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

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

@Chorer
Copy link

Chorer commented Jul 29, 2021

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

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

@zhuokuang
Copy link

zhuokuang commented Dec 13, 2021

``> ```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: [] }
最后在大佬们的回复中找到了解决方案,感谢提供解决方案的大佬们!

@Benjamin-Zh
Copy link

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

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

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

@summerhll
Copy link

summerhll commented Aug 12, 2022 via email

@yurnery
Copy link

yurnery commented Oct 6, 2022

``> ```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+ 测试

@summerhll
Copy link

summerhll commented Oct 6, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests