关于前端:minipromise-实现原理讲解

55次阅读

共计 8969 个字符,预计需要花费 23 分钟才能阅读完成。

文章首发于我的博客 https://github.com/mcuking/bl…

实现源码请查阅 https://github.com/mcuking/bl…

本文次要是论述如何一步步实现一个合乎 Promises/A+ 标准 的 Promise。

Promise 实现

在实现之前,让咱们先看一段应用 Promise 的示例代码:

new Promise((resolve, reject) => {setTimeout(resolve('hello world'), 1000);
})
.then((msg) => console.log(msg), 
  (err) => console.error(err)
);

Promise 实例化对象过程的实现

首先咱们来先实现前半部分的性能,即:

new Promise((resolve, reject) => {setTimeout(resolve('hello world'), 1000);
})

依据 Promises/A+ 标准 咱们能够合成一下须要做的事件:

  1. 首先采纳一个类来实现 Promise;
  2. 这个类的实例有一个状态属性,来示意 Promise 对象实例的三种状态:pending、resolved、rejected;
  3. Promise 实例化对象的时候会接管一个函数,会在实例化的时候就立刻执行;
  4. 上一点的立刻执行函数有两个参数:resolve 办法和 reject 办法。这两个办法次要是用来扭转 Promise 对象实例的状态,以及执行胜利 / 失败回调函数,这个逻辑放到前面来实现。

因而能够初步写出如下代码:

class Promise {constructor(executor) {if (typeof executor !== 'function') {throw new TypeError(`Promise resolver ${executor} is not a function`);
    }

    this.value = undefined; // Promise 的值
    this.status = 'pending'; // Promise 以后的状态

    const resolve = value => {// 胜利后的一系列操作(状态的扭转、胜利回调执行)};

    const reject = reason => {// 失败后的一系列操作(状态的扭转、失败回调执行)};

    try {
      // 思考到执行 executor 的过程中有可能出错,所以咱们用 try/catch 块给包起来,并且在出错后以 catch 到的值 reject 掉这个 Promise
      executor(resolve, reject); // 执行 executor
    } catch (e) {reject(e);
    }
  }
}

接下来咱们来剖析如何实现第二局部 –then 办法:

new Promise((resolve, reject) => {setTimeout(resolve('hello world'), 1000);
})
.then((msg) => console.log(msg), 
  (err) => console.error(err)
);

依据 Promises/A+ 标准,then 办法会接管两个参数,别离是胜利和失败的回调函数。当调用 resolve 办法将 Promise 状态从 pending 改为 resolved,并执行传入的胜利回调函数;调用 reject 办法将 Promise 状态从 pending 改为 rejected,并执行失败回调函数。

须要留神的是,一个 Promise 实例对象的 then 办法会被调用屡次,也就说 then 办法能够接管多个胜利 / 失败回调函数,所以须要应用数组来接管这些回调函数。当 Promise 实例对象的状态从 pending 变成 resovled/rejected 时,就遍历存储回调函数的数组执行所有的胜利 / 失败回调函数。

上面是对应的实现代码:

class Promise {constructor(executor) {if (typeof executor !== 'function') {throw new TypeError(`Promise resolver ${executor} is not a function`);
    }

    this.value = undefined; // Promise 的值
    this.status = 'pending'; // Promise 以后的状态
    this.onResolvedCallbacks = []; // Promise resolve 时的回调函数集,因为在 Promise 完结之前有可能有多个回调增加到它下面
    this.onRejectedCallbacks = []; // Promise reject 时的回调函数集,因为在 Promise 完结之前有可能有多个回调增加到它下面

    const resolve = value => {
      // 以后状态为 pending 时才会执行
      if (this.status === 'pending') {
        this.status = 'resolved';
        this.value = value;
        for (let i = 0; i < this.onResolvedCallbacks.length; i++) {this.onResolvedCallbacks[i](value);
        }
      }
    };

    const reject = reason => {
      // 以后状态为 pending 时才会执行
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.value = reason;
        for (let i = 0; i < this.onRejectedCallbacks.length; i++) {this.onRejectedCallbacks[i](reason);
        }
      }
    };

    try {
      // 思考到执行 executor 的过程中有可能出错,所以咱们用 try/catch 块给包起来,并且在出错后以 catch 到的值 reject 掉这个 Promise
      executor(resolve, reject); // 执行 executor
    } catch (e) {reject(e);
    }
  }
}

Promise 对象实例的 then 办法的实现

从下面的实现中,读者可能会有一个疑难: 执行 resolve 和 reject 办法时,为什么会须要先判断以后 Promise 实例对象状态是否为 pending。如果是 pending,才会遍历执行回调函数数组中的回调函数呢?

因为当 Promise 实例对象曾经处于 resolved 或 rejected 状态时,传入的对应回调函数时就须要被立刻执行,并且只会被执行一次。再次调用 resolve 或 reject 办法不会再做任何操作。

例如当 Promise 实例曾经处于 resolved 状态时,调用 then 办法接管胜利回调函数时,该函数会被立刻执行。同理处于 rejected 状态时,会立刻执行 then 办法接管的失败回调函数。

另外须要明确的一点是,依据 Promises/A+ 标准,then 办法必须返回一个 Promise 对象,因而执行完 then 办法后,须要再返回一个新的 Promise 实例。

那么咱们就来依据方才的剖析来实现一下 then 办法。上面对应的实现代码:

// 减少 then 办法
class Promise {constructor(executor) {// 代码同上}

  // then 办法接管两个参数,onResolved,onRejected,别离为 Promise 胜利或失败后的回调
  then(onResolved, onRejected) {
    let self = this;
    let promise2;

    // 依据规范,如果 then 的参数不是 function,则咱们须要疏忽它,此处以如下形式解决
    onResolved = typeof onResolved === 'function' ? onResolved : function(v) {};
    onRejected = typeof onRejected === 'function' ? onRejected : function(r) {};

    if (self.status === 'resolved') {return (promise2 = new Promise(function(resolve, reject) {onResolved(self.data);
      }));
    }

    if (self.status === 'rejected') {return (promise2 = new Promise(function(resolve, reject) {onRejected(self.data);
      }));
    }

    if (self.status === 'pending') {return (promise2 = new Promise(function(resolve, reject) {self.onResolvedCallbacks.push(function(value) {onResolved(value);
        });
        self.onRejectedCallbacks.push(function(reason) {onRejected(reason);
        });
      }));
    }
  }
}
  • 以后 promise1 处于 pending 状态时,执行 then 办法时返回一个 promise2 的同时,在 promise2 对应的立刻执行函数中将接管到的回调函数塞入 promise1 的回调队列中;
  • 当处于 resolved 时,则在返回的 promise2 对应的立刻执行函数中调用传入的胜利回调函数;
  • 当处于 rejeted 时,则在返回的 promise2 对应的立刻执行函数中调用传入的失败回调函数。

这里读者可能会有一个疑难,为什么须要在 promise2 对应的立刻执行函数中执行塞入回调函数到队列或立刻执行回调函数的逻辑?间接在里面执行这些逻辑不就能够了么?这就波及到了下个要实现的性能了 –Promise 链式调用

Promise 链式调用 - 回调函数返回值问题

依据 Promises/A+ 标准:

promise2 = promise1.then(onFulfilled, onRejected);   

如果 onFulfilled 或者 onRejected 返回一个值 x,则运行上面的 Promise 解决过程:[[Resolve]](promise2, x)。具体规定如下:

这里咱们只思考 x 为 Promise 或非对象和函数的值的状况:

如果 onResolved/onRejected 的返回值 x 是一个 Promise 对象,间接取它的后果做为 promise2 的后果,即 x 接管了 promise2 的状态:如果 x 处于 pending 状态,promise2 需放弃为 pending 状态直至 x 被 resolve/reject 掉,如果 x 处于 resolved 状态,用雷同的 value 执行 promise2,如果 x 处于 rejected 状态,用雷同的 reason 回绝 promise2。

如果 x 不为对象或者函数,则以 x 为参数执行 promise2。

上面是对应的实现代码:

// 针对上一个 Promise 为 pending 时,上一个 then 返回值进行优化
class Promise {constructor(executor) {// 同上}

  // then 办法接管两个参数,onResolved,onRejected,别离为 Promise 胜利或失败后的回调
  then(onResolved, onRejected) {
    let self = this;
    let promise2;

    // 依据规范,如果 then 的参数不是 function,则咱们须要疏忽它,此处以如下形式解决
    onResolved = typeof onResolved === 'function' ? onResolved : function(v) {};
    onRejected = typeof onRejected === 'function' ? onRejected : function(r) {};

    if (self.status === 'resolved') {// 如果 promise1( 此处即为 this/self) 的状态曾经确定并且是 resolved,咱们调用 onResolved
      // 因为思考到有可能 throw,所以咱们将其包在 try/catch 块里
      return (promise2 = new Promise(function(resolve, reject) {
        try {let x = onResolved(self.value);
          if (x instanceof Promise) {
            // 如果 onResolved 的返回值是一个 Promise 对象,间接取它的后果做为 promise2 的后果
            x.then(resolve, reject);
          }
          resolve(x); // 否则,以它的返回值做为 promise2 的后果
        } catch (e) {reject(e); // 如果出错,以捕捉到的谬误做为 promise2 的后果
        }
      }));
    }

    // 此处与前一个 if 块的逻辑简直雷同,区别在于所调用的是 onRejected 函数,就不再做过多解释
    if (self.status === 'rejected') {return (promise2 = new Promise(function(resolve, reject) {
        try {let x = onRejected(self.value);
          if (x instanceof Promise) {x.then(resolve, reject);
          }
          reject(x); // 否则,以它的返回值做为 promise2 的后果
        } catch (e) {reject(e);
        }
      }));
    }

    if (self.status === 'pending') {
      // 如果以后的 Promise 还处于 pending 状态,咱们并不能确定调用 onResolved 还是 onRejected,// 只能等到 Promise 的状态确定后,能力的确如何解决。// 所以咱们须要把咱们的 ** 两种状况 ** 的解决逻辑做为 callback 放入 promise1(此处即 this/self) 的回调队列里
      // 逻辑自身跟第一个 if 块内的简直统一,此处不做过多解释
      return (promise2 = new Promise(function(resolve, reject) {self.onResolvedCallbacks.push(function(value) {
          try {let x = onResolved(value);
            if (x instanceof Promise) {x.then(resolve, reject);
            }
          } catch (e) {reject(e);
          }
        });

        self.onRejectedCallbacks.push(function(reason) {
          try {let x = onRejected(reason);
            if (x instanceof Promise) {x.then(resolve, reject);
            }
          } catch (e) {reject(e);
          }
        });
      }));
    }
  }
}

Promise 链式调用 - 值透传问题

new Promise(resolve => {resolve(8)
})
  .then()
  .then()
  .then((value) => console.log(value))

从下面的 Promise 应用示例代码中,咱们会发现一个场景:在 Promise 链式调用中,当两头的 then 办法没有接管到回调函数时,前面的 then 办法接管的回调函数仍可能获取到后面传递的值。那么这里就须要 then 办法在接管的回调函数作如下操作,即如果没有传入胜利 / 失败回调函数时,默认的回调函数须要将接管的值返回给下一个 Promise 实例对象。上面是对应的实现代码:

then(onResolved, onRejected) {
    let self = this;
    let promise2;

    // 增加值的透传性能
    onResolved =
      typeof onResolved === 'function'
        ? onResolved
        : function(value) {return value;};
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : function(reason) {throw reason;};
    ......
}

结束语

到这里一个根本的 Promise 曾经实现了,上面是残缺的 Promise 代码实现。经验了整个过程,置信读者对 Promise 的了解会更加深刻了。

class Promise {constructor(executor) {if (typeof executor !== 'function') {throw new TypeError(`Promise resolver ${executor} is not a function`);
    }

    this.value = undefined; // Promise 的值
    this.status = 'pending'; // Promise 以后的状态
    this.onResolvedCallback = []; // Promise resolve 时的回调函数集,因为在 Promise 完结之前有可能有多个回调增加到它下面
    this.onRejectedCallback = []; // Promise reject 时的回调函数集,因为在 Promise 完结之前有可能有多个回调增加到它下面

    const resolve = value => {if (this.status === 'pending') {
        this.status = 'resolved';
        this.value = value;
        for (let i = 0; i < this.onResolvedCallback.length; i++) {this.onResolvedCallback[i](value);
        }
      }
    };

    const reject = reason => {if (this.status === 'pending') {
        this.status = 'rejected';
        this.value = reason;
        for (let i = 0; i < this.onRejectedCallback.length; i++) {this.onRejectedCallback[i](reason);
        }
      }
    };

    try {
      // 思考到执行 executor 的过程中有可能出错,所以咱们用 try/catch 块给包起来,并且在出错后以 catch 到的值 reject 掉这个 Promise
      executor(resolve, reject); // 执行 executor
    } catch (e) {reject(e);
    }
  }

  // then 办法接管两个参数,onResolved,onRejected,别离为 Promise 胜利或失败后的回调
  then(onResolved, onRejected) {
    let self = this;
    let promise2;

    // 增加值的透传性能
    onResolved =
      typeof onResolved === 'function'
        ? onResolved
        : function(value) {return value;};
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : function(reason) {throw reason;};

    if (self.status === 'resolved') {// 如果 promise1( 此处即为 this/self) 的状态曾经确定并且是 resolved,咱们调用 onResolved
      // 因为思考到有可能 throw,所以咱们将其包在 try/catch 块里
      return (promise2 = new Promise(function(resolve, reject) {
        try {let x = onResolved(self.value);
          if (x instanceof Promise) {
            // 如果 onResolved 的返回值是一个 Promise 对象,间接取它的后果做为 promise2 的后果
            x.then(resolve, reject);
          } else {resolve(x); // 否则,以它的返回值做为 promise2 的后果
          }
        } catch (e) {reject(e); // 如果出错,以捕捉到的谬误做为 promise2 的后果
        }
      }));
    }

    // 此处与前一个 if 块的逻辑简直雷同,区别在于所调用的是 onRejected 函数,就不再做过多解释
    if (self.status === 'rejected') {return (promise2 = new Promise(function(resolve, reject) {
        try {let x = onRejected(self.value);
          if (x instanceof Promise) {x.then(resolve, reject);
          } else {reject(x); // 否则,以它的返回值做为 promise2 的后果
          }
        } catch (e) {reject(e);
        }
      }));
    }

    if (self.status === 'pending') {
      // 如果以后的 Promise 还处于 pending 状态,咱们并不能确定调用 onResolved 还是 onRejected,// 只能等到 Promise 的状态确定后,能力的确如何解决。// 所以咱们须要把咱们的 ** 两种状况 ** 的解决逻辑做为 callback 放入 promise1(此处即 this/self) 的回调队列里
      // 逻辑自身跟第一个 if 块内的简直统一,此处不做过多解释
      return (promise2 = new Promise(function(resolve, reject) {self.onResolvedCallback.push(function(value) {
          try {let x = onResolved(value);
            if (x instanceof Promise) {x.then(resolve, reject);
            } else {resolve(x);
            }
          } catch (e) {reject(e);
          }
        });

        self.onRejectedCallback.push(function(reason) {
          try {let x = onRejected(reason);
            if (x instanceof Promise) {x.then(resolve, reject);
            } else {reject(x);
            }
          } catch (e) {reject(e);
          }
        });
      }));
    }
  }
}

参考资料

  • Promises/A+ 标准
  • Promises/A+ 标准(译)

正文完
 0