首页 > 技术文章 > promise的实现方式和运行机制

sowhite 2018-04-25 18:51 原文

promise的规范其实种类很多,我们最常用的是promise/A+

这篇文章会先将一个类似于promise的构造函数怎么写,网上很多教程并没有实现A+规范,只是看起来像而已

然后我们深入探究如何一步一步实现真正的promise/A+规范的要求。

首先我们实现一个简单的promise,这个promise类似于express的路由处理

express的路由处理以next的方式一层一层的进入, 直到不再next。这个很像promise呐,也是比较懒,没有去看源码,但试着实现一个那种异步,会发现其实蛮简单的。

ok,废话少说,先搞一个

 

需求

1. promise拥有then和catch方法

2. promise出错时调用catch,有两种错误,主动跑错可以用next(err)

3. promise的第一个参数为function, 第一个参数为next,其他参数可以传递到下一步。

然后我们要实现的功能如下

new Promise(next => {
  setTimeout(() => {
    console.log(1);
    next(null, 2);
  }, 1000);
})
  .then((next, arg) => {
    setTimeout(() => {
      console.log(arg);
      next();
    }, 1000);
  })
  .then((next, arg) => {
    setTimeout(() => {
      console.log(arg);
      next();
    }, 1000);
  })
  .catch(err => console.error(err));

 

实现

首先我们实现then和catch方法

  我们建立一个回调队列和一个出错回调1

  1)then里的东西都会被推进去

  2)catch会去替换默认的回调方法

  3)next函数会执行resovleCallbacks里的下一个方法 

function Promise(execute) {
  let resolveCallbacks = [];
  let rejectCallbacks = err => console.error(err);
  this.then = fn => {
    resolveCallbacks.push(fn);
    return this;
  };
  this.catch = fn => {
    rejectCallback = fn;
    return this;
  };

  execute(next);

  function next() {
    const nextCallback = resolveCallbacks.shift();
    nextCallback && nextCallback(next);
  }
}

为了捕捉错误 

  try {
    execute(next);
  } catch (err) {
    rejectCallback(err);
  }

我们添加如上的错误捕捉机制

假如果主动跑错就用next(err)

没有出错的话就next(null, arg1, arg2)这样传参

所以我们修改next方法

  function next(...args) {
    if(args[0]){
        return rejectCallback(args[0]);
    }else{
        const nextCallback = resolveCallbacks.shift();
        try {
          nextCallback && nextCallback(next, ...args.slice(1));
        } catch (err) {
          rejectCallback(err);
        }
    }
  }

 

至此就实现完成了,很简单吧

 game over

 

Promise/A+

但事实上一个伟大的promise/A+规范的实现可不止这么简单。

然后我们来看看一个promise/A+规范是什么样子的

1. 状态管理pendding fullfilled(resolved) rejected 三种

2. then返回一个新的promise, then的两个参数是function,是上一步的成功和失败的回调

3. 不同的promise可以接起来使用

以上是主要的规范 更加具体的看这里

 

要想实现promise/A+,首先要搞清楚它的工作原理

按照规范 最麻烦的是then方法的处理,

但是记住两点就Ok

1. 不同的promise的状态不同,所以then方法不可以返回this,而是应该返回一个新的promise

2. then的执行实在resolve完成后,所以在then方法里把onResolved方法修改为then的第一个参数

3. 在promise中then里的函数如果返回值是promise就必须等到promise执行完成才可以到下一步, 对此我们巧妙的运用promise的then,只要把resolve放到then里边就会延迟resolve的发生

return x.then(resolve);

 

4. 在promise中then里的函数如果返回值不是promise那就直接执行 然后resolve

5. executor应该是慢一点开始,所以用了setTimeout,因为首先需要执行的是then方法,待一切promise队列就绪后才可以开始。

6. 执行then的时候可能已经resolved了,那么这个时候就立即执行,如果还是pending状态的话那么就放到执行完成的回调函数里。

知道以上几点之后 就可以很轻松的看懂代码了

首先是构造函数

function Promise(executor) {
    let onResolved = null;
    let onRejected = null;
    this.status = 'pending';
    let self = this;

    let resolve = data => {
        self.data = data || null;
        self.status = 'resolved';
        typeof onResolved === 'function' && onResolved(data);
    }

    let reject = (err) => {
        self.status = 'rejected';
        typeof onRejected === 'function' && onRejected(data);
    }

    this.then = onResolvedFn => {
        
    }

    executor(resolve, reject);
}

 

然后是then方法

    if (self.status === 'pending') {
            console.log('promise is pending, add callback')
            return new Promise(resolve => {
                onResolved = data => {
                    let x = onResolvedFn(self.data)
                    if (x instanceof Promise) {
                        return x.then(resolve);
                    } else {
                        resolve();
                    }
                }
            })
        }
        
        if (self.status === 'resolved') {
            console.log('promise is resolved, execuate')
            return new Promise(resolve => {
                let x = onResolvedFn(self.data)
                if (x instanceof Promise) {
                    return x.then(resolve);
                } else {
                    resolve();
                }
            })
        }

 

then方法实现后,然后我们实现一个Promise.resolve方法

Promise.resolve = data => new Promise(resolve => setTimeout(() => resolve(data)));

关于catch和promise的错误处理机制这里就不详细的写了,反正一切都为了简单看懂。

 

参考文章: https://zhuanlan.zhihu.com/p/21834559

 

推荐阅读