美文网首页前端译趣
纯手写实现自己的nodejs promise 库

纯手写实现自己的nodejs promise 库

作者: linc2046 | 来源:发表于2018-05-24 10:04 被阅读2次

纯手写实现自己的nodejs promise 库

Promise是js推荐的异步原生元素。
回调函数变得越来越少,特别是现在nodejs已经可以用async/await。

async/await也是基于promise的,所以你需要理解primose才能掌握async/await.

本文将会带你编写自己的promise库,以及如何使用async/await.

什么是Promise?

es6标准中,promise是一个构造函数接受运算函数的类。

promise类的实例拥有then方法。
根据标准promise还有其他的熟悉, 单本教程中你可以暂时忽略。

以下是简单promise类的简单形式。

class MyPrimise {
  constructor(executor) {}

  then(onFullfilled, onRejected) {}
}

运算函数接受两个参数, resolve函数和reject函数。
promise是有3种状态的状态机。

  • 等待: 初始状态,意味着promise还未建立
  • 完成: 底层操作已经成功,并存在关联值
  • 拒绝: 底层操作失败,有关联错误

记住这些,实现第一版的MyPromise构造函数很简单。

constructor(executor){
  if(typeof executor !== 'function'){
    throw new Error();
  }

  this.$state = 'PENDING';
  this.$chained = [];

  const resolve = res => {
    if(this.$state !== 'PENDING'){
      return;
    }
  }
}

then函数更简单。记住then函数接受两个参数, onFullfilled 和 onRejected。

then函数负责确保promise完成时调用onFullfilled, promise失败时调用onRejected.

如果Promise已经解决或拒绝, then 应该立即调用onFullfilled 或onRejected。

如果promise仍在等待,then应该将参数推入$chained数组供resolve和reject函数调用。

then(onFulfilled, onRejected) {
  if (this.$state === 'FULFILLED') {
    onFulfilled(this.$internalValue);
  } else if (this.$state === 'REJECTED') {
    onRejected(this.$internalValue);
  } else {
    this.$chained.push({ onFulfilled, onRejected });
  }
}
  • 另外,es特性中表示调用已经解决或拒绝的promise上的then方法,意味着 onFullfilled 或者 onRejecte函数应该在下次调用。

由于本文的代码会实现简版而不是标准的完整实现,本实现将会忽略该细节。

promise 链

以上实例特别湖绿了最复杂也是最有用的部分: 链式调用。

链式调用的实现是如果onFullfilled或onRejected函数返回promise, then方法应该返回一个被锁住的新promise以匹配返回promise的状态。例如:

p = new MyPromise(resolve => {
  setTimeout(() => resolve('World'), 100);
});

p.
  then(res => new Promise(resolve => resolve(`Hello, ${res}`))).
  then(res => console.log(res));

以下是返回新promise的新then函数,用于链式调用。

then(onFullfilled, onRejected) {
  return new MyPromise((resolve, reject) => {
    const _onFullfilled = res => {
      try {
        resolve(onFullfilled(res));
      } catch(ex){
        reject(ex);
      }
    };

    const _onRejected = err => {
      try{
        reject(onRejected(err));
      } catch(ex){
        reject(ex);
      }
    };

    if(this.$state === 'FULLFILLED'){
      _onFullfilled(this.$internalValue);
    } else if( this.$state === 'REJECTED'){
      _onRejected(this.$internalValue);
    } else {
      this.$chained.push({
        onFullfilled: _onFullfilled,
        onRejected: _onRejected
      });
    }
  });
}

现在then返回一个新promise, 但是还需要完善。
如果onFullfilled返回promise, resolve需要能够处理promise.
为了支持这,rsolve函数需要使用then方法进行两步递归调用。

以下是扩展的resolve函数.

const resove = res => {
  if(this.$state !== 'PENDING'){
    return;
  }

  if(res != null && typeof res.then === 'function'){
    return res.then(resolve, reject);
  }

  this.$state = 'FULLFILLED';
  this.$internalValue = res;

  for(const {onFullfilled} of this.$chained) {
    onFullfilled(res);
  }

  return res;
}

为了保持简洁,以上实例实现了关键部分,只要promise被锁住以匹配另外的promise, 调用resolve或reject就是空调用。

以上实例中,你可以对等待promise调用resolve,抛错, 之后res.then(resolve, reject)也会是空函数。 这只是个例子,没有完全实现ES6 promise标准。

以上代码显示已解决的promise和已完成的promise之间的区别。
差异很小,主要和链式调用有关。
已解决其实并不是真正的promise状态,但它是ES6特性中定义的术语。

当你resolve以上实现,下面之一会发生:

  • 如果你调用resolve(v), v不是promise, 那么promise会立刻变成已完成状态。这种情况下,已解决和已完成是一样的。

  • 如果你调用resolve(v), v是另一个promise, 这个promise仍然是等待状态,直到v resolve或rejec。 这种情况下,promise已解决,但仍然是等待状态。

Async/Await

记住await关键字暂停异步函数执行,直到被延迟的promise执行完成。

现在你已经实现了家用promise库,我们看下和async/await一起使用会发生什么。
在then函数中加入console.log语句。

then(onFullfilled, onRejected) {
  console.log('Then'), onFullfilled, onRejected, new Error().stack);

  return new MyPromise((resolve, reject) => {

  });
}

现在,让我们等待MyPromise的实例,看看会发生什么。

run().catch(error => console.error(error.stack));

await function run(){
  const start = Date.now();
  await new MyPromise(resolve => setTimeout(() => resolve(), 100));
  console.log('Elapsed Time', Date.now() - start);
}

记录上面catch函数调用。

catch函数是ES6 promise特性的核心部分。
本文不会详细介绍,因为 catch(f) 等同于 .then(null, f), 没有太多不同。

以下是输出。注意await 隐式调用 then方法, onFullfilled和OnRejected方法会深入到V8引擎的内部中。
另外, await 在下次调用then方法前总是等待。

Then function () { [native code] } function () { [native code] } Error
    at MyPromise.then (/home/val/test/promise.js:63:50)
    at process._tickCallback (internal/process/next_tick.js:188:7)
    at Function.Module.runMain (module.js:686:11)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:608:3
Elapsed time 102

后续

Async/await很强大,只有理解promise基础,才能掌握。
Promise也有很多缺点,如: 异步函数执行的同步错误无法获取,
promise结束后内部状态无法改变,这也使得async/await成为可能。

一旦你完全理解了promise, async\await会变得更加容易。

译者注

  • 原文链接

  • 因译者水平有限,如有错误,欢迎指正交流

相关文章

  • 纯手写实现自己的nodejs promise 库

    纯手写实现自己的nodejs promise 库什么是Promise?promise 链Async/Await后续...

  • nodejs + cheerio + Promise(blueb

    文章概要 使用nodejs + cheerio + Promise(bluebird库实现)的nodejs课程数据...

  • 实现一个简单的Promise/Deferred框架

    利用nodejs的event模块,可以实现一个最简单的Promise/Deferred框架: Promise De...

  • 手写Promise实现

    初学ES 6的Promise之后, 感觉这个新的异步处理模型有点绕, 为了加深理解, 所以尝试手写Promise的...

  • 手写Promise的实现

    1. 简洁版 (没有注释) 2. 详细注释版 3. 测试 4. 简单的伪代码 Promise实现伪代码 定义三种状...

  • axios的用法详解

    axios: 是一个类库,基于Promise管理的Ajax库,支持浏览器和nodejs的http库,常用于Ajax...

  • 手写promise

    本章节纯粹是对promise手写实现。如果不了解promise自行了解再来学习本章知识。promise初体验首先,...

  • 手写promise

    promise 手写 代码来源:darrell-wheels promise 30分钟,带你实现一个符合规范的 P...

  • 手写Promise

    手写 Promise 我们会通过手写一个符合 Promise/A+ 规范的 Promise 来深入理解它,并且手写...

  • JS 手写Promise

    二、手写Promise 1.Promise的设计和构造方法 2.then方法的设计 3.then方法的优化一 实现...

网友评论

    本文标题:纯手写实现自己的nodejs promise 库

    本文链接:https://www.haomeiwen.com/subject/urqdjftx.html