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

第 10 题:简单手写实现promise #10

Open
airuikun opened this issue Apr 8, 2019 · 7 comments
Open

第 10 题:简单手写实现promise #10

airuikun opened this issue Apr 8, 2019 · 7 comments

Comments

@airuikun
Copy link
Owner

airuikun commented Apr 8, 2019

       // 简易版本的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);
        }
@medsciJs
Copy link

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

@ShellWolf
Copy link

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

@nelsonkuang
Copy link

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

@biubiupiu1
Copy link

这里不支持多层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 做了如下改动,感觉是不需要轮询的

@davidhuangdw
Copy link

附加需求:

  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("===========");

@wlw620
Copy link

wlw620 commented Jun 13, 2019

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

@lumixraku
Copy link

附加需求:

  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 表现不一致了

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

8 participants