technology-blog icon indicating copy to clipboard operation
technology-blog copied to clipboard

第 10 题:简单手写实现promise

Open airuikun opened this issue 5 years ago • 7 comments

       // 简易版本的promise 
        // 第一步: 列出三大块  this.then   resolve/reject   fn(resolve,reject)
        // 第二步: this.then负责注册所有的函数   resolve/reject负责执行所有的函数 
        // 第三步: 在resolve/reject里面要加上setTimeout  防止还没进行then注册 就直接执行resolve了
        // 第四步: resolve/reject里面要返回this  这样就可以链式调用了
        // 第五步: 三个状态的管理 pending fulfilled rejected
     
        // *****promise的链式调用 在then里面return一个promise 这样才能then里面加上异步函数
        // 加上了catch
        function PromiseM(fn) {
            var value = null;
            var callbacks = [];
            //加入状态 为了解决在Promise异步操作成功之后调用的then注册的回调不会执行的问题
            var state = 'pending';
            var _this = this;

            //注册所有的回调函数
            this.then = function (fulfilled, rejected) {
                //如果想链式promise 那就要在这边return一个new Promise
                return new PromiseM(function (resolv, rejec) {
                    //异常处理
                    try {
                        if (state == 'pending') {
                            callbacks.push(fulfilled);
                            //实现链式调用
                            return;
                        }
                        if (state == 'fulfilled') {
                            var data = fulfilled(value);
                            //为了能让两个promise连接起来
                            resolv(data);
                            return;
                        }
                        if (state == 'rejected') {
                            var data = rejected(value);
                            //为了能让两个promise连接起来
                            resolv(data);
                            return;
                        }
                    } catch (e) {
                        _this.catch(e);
                    }
                });
            }

            //执行所有的回调函数
            function resolve(valueNew) {
                value = valueNew;
                state = 'fulfilled';
                execute();
            }

            //执行所有的回调函数
            function reject(valueNew) {
                value = valueNew;
                state = 'rejected';
                execute();
            }

            function execute() {
                //加入延时机制 防止promise里面有同步函数 导致resolve先执行 then还没注册上函数
                setTimeout(function () {
                    callbacks.forEach(function (cb) {
                        value = cb(value);
                    });
                }, 0);
            }

            this.catch = function (e) {
                console.log(JSON.stringify(e));
            }

            //经典 实现异步回调
            fn(resolve, reject);
        }


airuikun avatar Apr 08 '19 08:04 airuikun

new PromiseM((res, rej) => { setTimeout(() => { rej() },0) }).then(function(){ console.log(111) }, function(){ console.log(222); }) //111

medsciJs avatar Apr 10 '19 03:04 medsciJs

大神思路很清晰,不过上面代码还是有些bug 1:then 方法里面,在最初始的pending状态,只push了fulfilled的回调函数,就会导致,无论是res还是rej,打印的都是111,理论上,上述例子打印应该是222,callbacks可以分成2份进行存储

ShellWolf avatar Apr 16 '19 08:04 ShellWolf

这里不支持多层then链式调用(then中可返回新的promise进行异步流控制),不支持finally,我写了个版本,多多指教 http://www.a4z.cn/fe/2019/04/18/weekly-fe-interview/

nelsonkuang avatar Apr 18 '19 10:04 nelsonkuang

这里不支持多层then链式调用(then中可返回新的promise进行异步流控制),不支持finally,我写了个版本,多多指教 http://www.a4z.cn/fe/2019/04/18/weekly-fe-interview/


then: function (onFulfilled, onRejected) {
            var _this = this

            if (_this.promiseStatus == 'pending') {
                return new Promise2(function (resolve, reject) {
                    onFulfilled && (_this.onFulfilledCb = function () {
                        try {
                            var x = onFulfilled(_this.promiseValue);
                            if (x instanceof Promise2) {
                                x.then(resolve, reject);
                            } else {
                                resolve(x);
                            }
                        } catch (e) {
                            reject(e)
                        }
                    });
                    onRejected && (_this.onRejectedCb = function () {
                        try {
                            var x = onRejected(_this.promiseValue);
                            if (x instanceof Promise2) {
                                x.then(resolve, reject);
                            } else {
                                resolve(x);
                            }
                        } catch (e) {
                            reject(e)
                        }
                    })

                })
            }

            if (_this.promiseStatus == 'fulfilled') {
                return new Promise2(function (resolve, reject) {
                    try {
                        var x = onFulfilled(_this.promiseValue);
                        if (x instanceof Promise2) {
                            x.then(resolve, reject);
                        } else {
                            resolve(x);
                        }
                    } catch (e) {
                        reject(e)
                    }
                })
            }

            if (_this.promiseStatus == 'rejected') {
                return new Promise2(function (resolve, reject) {
                    try {
                        var x = onRejected(_this.promiseValue);
                        if (x instanceof Promise2) {
                            x.then(resolve, reject);
                        } else {
                            resolve(x);
                        }
                    } catch (e) {
                        reject(e)
                    }
                })
            }
        },

对你的实现then 做了如下改动,感觉是不需要轮询的

biubiupiu1 avatar May 06 '19 13:05 biubiupiu1

附加需求:

  1. catch完允许恢复
  2. then/catch的回调允许返回Promise
let isThenable = x => !!(x!==undefined && x.then);
let runThenable = (func, arg) => isThenable(arg) ? arg.then(func) : func(arg);

class MyPromise{
  constructor(f){
    this.succ_que = [];
    this.fail_que = [];
    this.done = false;

    this.resolve = result => {
      if(this.done) return;
      this.result = result;
      this.done = true;
      setImmediate(() => {          // setImmediate to prevent children from being caught by parent
        this.succ_que.forEach(cb => cb(result));
      });
    };

    this.reject = error => {
      if(this.done) return;
      this.error = error;
      this.done = true;
      setImmediate(() => {
        this.fail_que.forEach(cb => cb(error));
      });
    };

    this.then = (succ_cb, fail_cb) => new MyPromise((next_resolve, next_reject) => {
      let handle_result = v => {
        try{
          runThenable(next_resolve, succ_cb ? succ_cb(v) : v);    // runThenable to allow succ_cb/fail_cb return a promise
        } catch (e) {
          next_reject(e)
        }
      };
      let handle_error = e => {
        try{
          if(fail_cb)
            runThenable(next_resolve, fail_cb(e));    // resume after caught by fail_cb
          else
            next_reject(e);
        }catch (e) {
          next_reject(e)
        }
      };

      if(this.done){
        this.error ? handle_error(this.error) : handle_result(this.result)
      }else{
        this.succ_que.push(handle_result);
        this.fail_que.push(handle_error);
      }
    });

    this.catch = fail_cb => this.then(null, fail_cb);

    try{
      f(this.resolve, this.reject);
    }catch (e) {
      this.reject(e);
    }
  }
}
MyPromise.resolve = x => new MyPromise(r => r(x));

MyPromise.Any = (...promises) => {
  let done = 0;
  return new MyPromise(r => {
    promises.forEach(p => p.then(v =>{ if(!done){done++; r(v);} }));
  })
};
MyPromise.All = (...promises)=>{
  let count = promises.length;
  let values = [];

  return new MyPromise((r, f) => {
    promises.forEach((p, i) =>
      p.then(v => {
        values[i] = v;
        count --;
        if(count === 0) r(values);
      }, f)
    )
  });
};

// test:
MyPromise.resolve(1)
  .then(v=> v+1)
  .catch(e => console.log(`won't happen error: ${e}`))
  .then(v => {console.log(`continued: ${v}`); throw new Error("throw");})
  .then(v => {console.log("won't happen then");})
  .catch(e => {console.log(`catched: ${e}`); return 100;})
  .then(v => {console.log(`continue after catch: ${v}`); return v;})
  .then(v => new MyPromise(r=> setTimeout(() => r(v+500), 3000)))
  .then(v => console.log(`last: ${v}`))
;
console.log("===========");

davidhuangdw avatar May 16 '19 06:05 davidhuangdw

有几个疑问哈... 如果同步链式then调用 2次肯定都是pending 但是 callbacks 里面只push了 fulfilled,这样导致链式调用失效了,简单粗暴解决 callbacks 里面 callbacks.push(fulfilled, resolv) 不知可行否 还有就是这里没有区分 reject 情况吧, 按照这个例子来是不是要区分 resolve callback 和 reject callback ?

wlw620 avatar Jun 13 '19 02:06 wlw620

附加需求:

  1. catch完允许恢复
  2. then/catch的回调允许返回Promise
let isThenable = x => !!(x!==undefined && x.then);
let runThenable = (func, arg) => isThenable(arg) ? arg.then(func) : func(arg);

class MyPromise{
  constructor(f){
    this.succ_que = [];
    this.fail_que = [];
    this.done = false;

    this.resolve = result => {
      if(this.done) return;
      this.result = result;
      this.done = true;
      setImmediate(() => {          // setImmediate to prevent children from being caught by parent
        this.succ_que.forEach(cb => cb(result));
      });
    };

    this.reject = error => {
      if(this.done) return;
      this.error = error;
      this.done = true;
      setImmediate(() => {
        this.fail_que.forEach(cb => cb(error));
      });
    };

    this.then = (succ_cb, fail_cb) => new MyPromise((next_resolve, next_reject) => {
      let handle_result = v => {
        try{
          runThenable(next_resolve, succ_cb ? succ_cb(v) : v);    // runThenable to allow succ_cb/fail_cb return a promise
        } catch (e) {
          next_reject(e)
        }
      };
      let handle_error = e => {
        try{
          if(fail_cb)
            runThenable(next_resolve, fail_cb(e));    // resume after caught by fail_cb
          else
            next_reject(e);
        }catch (e) {
          next_reject(e)
        }
      };

      if(this.done){
        this.error ? handle_error(this.error) : handle_result(this.result)
      }else{
        this.succ_que.push(handle_result);
        this.fail_que.push(handle_error);
      }
    });

    this.catch = fail_cb => this.then(null, fail_cb);

    try{
      f(this.resolve, this.reject);
    }catch (e) {
      this.reject(e);
    }
  }
}
MyPromise.resolve = x => new MyPromise(r => r(x));

MyPromise.Any = (...promises) => {
  let done = 0;
  return new MyPromise(r => {
    promises.forEach(p => p.then(v =>{ if(!done){done++; r(v);} }));
  })
};
MyPromise.All = (...promises)=>{
  let count = promises.length;
  let values = [];

  return new MyPromise((r, f) => {
    promises.forEach((p, i) =>
      p.then(v => {
        values[i] = v;
        count --;
        if(count === 0) r(values);
      }, f)
    )
  });
};

// test:
MyPromise.resolve(1)
  .then(v=> v+1)
  .catch(e => console.log(`won't happen error: ${e}`))
  .then(v => {console.log(`continued: ${v}`); throw new Error("throw");})
  .then(v => {console.log("won't happen then");})
  .catch(e => {console.log(`catched: ${e}`); return 100;})
  .then(v => {console.log(`continue after catch: ${v}`); return v;})
  .then(v => new MyPromise(r=> setTimeout(() => r(v+500), 3000)))
  .then(v => console.log(`last: ${v}`))
;
console.log("===========");

对于这里的 Promise then 里面 done 部分处理有点不解。 这样处理会使得同步resolve和 Native 的 Promise 不一样。 new Promise((resolve, reject) => { result.push(1); resolve(2); result.push(3); }).then((d) => { result.push(d); }); setTimeout(() => { result.push(4); }, 0); result.push(5);

最后结果应该是 1, 3, 5, 2, 4

如果在then 里面因为是同步操作就done的话 then 的 cb 不会进入到 succ_cb中 输出结果是 1, 3, 2, 5, 4 这样和native 的promise 表现不一致了

lumixraku avatar Sep 09 '20 14:09 lumixraku