Make your own Promise

做出自己的承诺,而不是背诵脍炙人口的誓言

Posted by Lorry on July 8, 2018

文章字数:5656, 阅读全文大约需要:16 分钟

学习完event loop之后,对promise也有了新的认识,现在想自己写一个Promise的实现方式.

resolve的功能:

  • 改变promise状态为onfulfilled
  • 储存resolve(…)中的值
  • 判断当前是否存在then(…)注册回调函数,如果存在则异步执行onRe

    核心:

  • 利用DOM Mutation 实现 microtask

步骤:

1 定义状态:一共四种

/**
 * 0 -- pending
 * 1 -- onfulfilled
 * 2 -- rejected
 * 3 -- adopt another promise as _value
 */

2 定义Promise类

一般都会在新建promise时传fn为参数. 实例变量包含

  • state –> 表示Promise状态
  • value –> 表示resolve的值
  • defers –> 表示待执行的回调 原型方法有then,solve,reject,all,race(先实现then)
    class Promise {
      constructor(fn) {
          if (typeof fn !== 'function') {
              throw new TypeError('Promise constructor\'s argument is not a function');
          }
          this.state = 0;
          this.value = null
          this.defers = [];
          if( fn === noop ) return;
          doResolve(fn, this)
      }
      then(onFulfilled, onRejected) {
          let res = new Promise(noop);
          handle(this, new Handler(onFulfilled, onRejected, res))
          return res;
      }
    }
    

    3 定义Handler – 处理方法

    包含三种属性

  • onFulfilled
  • onRejected
  • promise
class Handler {
  constructor(onFulfilled, onRejected, promise) {
    this.onFulfilled = onFulfilled;
    this.onRejected = onRejected;
    this.promise = promise
  }
}

4 定义doResolve,处理resolve和reject过程, 改变Promise状态

// use try--catch
function tryCallTwo(fn, a, b) {
    try {
        fn(a, b);
    } catch (ex) {
        GLOBAL_ERROR = ex;
        return IS_ERROR;
    }
}
function doResolve(fn, promise) {
    let done = false;
    let res = tryCallTwo(fn, 
    (value) => {
        // guarantee resolve or reject only call once 
        if (done) return;
        done = true;
        resolve(promise, value)},
    (reason) => {
        if (done) return;
        done = true;
        reject(promise, reason)
    });
    // will not call any of resolve or reject
    if(!done && res === IS_ERROR) {
        done = true;
        reject(promise, GLOBAL_ERROR)
    }
}

请注意这里的if(done) return;的作用,虽然只有一句话.请看下面的例子

let a = new Promise((res, rej) => {
    res('resolved');
    console.log('continue');
    rej('reject');
})
a.then(res => console.log(res));

会打印continue, resolved 不会打印reject, 直接return掉了.

5 三个Promise的过程,resolve, reject, finale

/** use try-catch
 * if some object has then method, it will be regarded as one promise
 * such as let a = {then: resolve =>resolve(123)}
 * let b = new Promise((resolve)=>resolve(a));
 * b.then(v=>console.log(v)) // v = 123
*/
function getThen(obj) {
    try {
        return obj.then
    } catch(ex) {
        GLOBAL_ERROR = ex;
        return IS_ERROR
    }
}
function resolve(promise, newValue) {
    // cannot be same one
    if (newValue === promise) {
        return reject(promise, new TypeError('a promise cannot resolve itself'))
    }
    if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
        var then = getThen(newValue);
        if (then === IS_ERROR) {
            return reject(promise, GLOBAL_ERROR);
        }
        /**
         *  to handle resolve which is one Promise,such as
         *  let a = new Promise((res)=>res(111))
         *  let b = new Promise((res) => res(a))
         *  (then === promise.then is to judge same constructor)
         */
        if(then === promise.then && newValue instanceof Promise) {
            promise.state = 3;
            promise.value = newValue;
            finale(promise);
            return;
        } 
        // mimic promise: eg. let a = {then: (resolve, reject)=>resolve(123)}
        else if (typeof then === 'function') {
            doResolve(then.bind(newValue), promise);
            return;
        }
    }
    // change the state of promise
    promise.state = 1;
    // assign resolve(value) value to promise value
    promise.value = newValue;
    finale(promise)
}
function reject(promise, reason) {
    promise.state = 2;
    promise.value = reason;
    finale(promise);
}

function finale(promise) {
    let len = promise.defers.length
    // handle the promise from defers
    if (len > 0) {
        for (let i = 0; i < len; i ++){
            handle(promise, promise.defers[i])
        }
        promise.defers = null;
    }
}

6 定义handle方法:用于处理回调

function handle(promise, defer) {
    // if promise.value is a promise, the new resolved promise will assign to it until not promise anymore.
    while (promise.state === 3) {
        promise = promise.value
    }
    // for handle extend
    if (Promise._onHandle){Promise._onHandle(promise)};
    // if state is pending(not resolve at once)
    if (promise.state === 0) {
        // add defer including callback in defers queue
        promise.defers.push(defer)
        return;
    }
    // async call
    handleResolve(promise, defer)
}

7 定义 handleResolve, 在此之前的代码全部都是同步的,只有这里才是异步,异步的实现为使用DOM Mutation,如果不支持,则使用setTimeout或setInterval

// use try--catch
function tryCallOne(fn, a) {
    try {
      return fn(a);
    } catch (ex) {
      GLOBAL_ERROR = ex;
      return IS_ERROR;
    }
}
function handleResolve(promise, defer) {
    asap(function() {
        let cb = promise.state === 1 ? defer.onFulfilled : defer.onRejected;
        // if no callback in then method
        if (cb === null) {
            promise.state === 1 ? resolve(defer.promise, promise.value) : reject(defer.promise, promise.value);
            return;
        }
        // if callback exists
        let ret = tryCallOne(cb, promise.value);
        
        ret === IS_ERROR ? 
            reject(defer.promise, GLOBAL_ERROR) :
            resolve(defer.promise, ret)
    })
}

Promise最大的用处便在于异步,异步过程实际上只在上述过程的handleResolve中.

resolve有两种情况

  • 同步的resolve, 这种情况promise.defers里面不会有任何东西,then中的回调会立即被塞到异步队列中.
  • 异步的resolve, 这种情况promise.defers会将then中的所有回调放到队列里,此时then中的回调并没有到异步队列中,只有当异步的resolve执行之后, 所有的回调再同步的被遍历,再被加到异步队列中.

注意then的链式调用:

链式调用流程

每一次的promise resolve会检测是否有defer,如果有则将value传给defer中的onFulfilled,直到链中最后一个promise为止