【译】Async-Await≈Generators+Promises

Async-Await ≈ Generators + Promisesjavascript

这篇文章我将介绍ES2017async函数为何是ES2016GeneratorsPromises特性功能的语法糖。java

阅读须知

  • 本文不对三者概念进行介绍和讲解
  • 本文惟一的目的就是介绍如何利用GeneratorsPromises去实现async
  • 本文对async和其余类似实现不进行优劣评价
  • 本文代码都是通过巧妙设计以便于理解,他们不适用于实际开发

为何?

既然async函数已被原生支持,还有理解它工做原理的必要吗?git

呃,除了由于好奇它的原理以外,更重要的是为了去支持旧的运行平台。若是你但愿使用了新功能的代码能够运行在旧的浏览器版本和Node.js版本,你可能须要使用诸如Babel这样的工具去转换这些新特性。github

所以,深入理解async函数如何被分解成generatorspromises后,在你阅读或调试转换后的代码能派上很大用场。好比,这是一个简单的async函数:promise

Babel转换成ES2016代码以下(不用彻底看懂,下文会解释):浏览器

二者差别很大!固然,若是你理解了async的工做原理,那么这段转换以后的代码对你来讲也是小菜一碟。ecmascript

另外一个有趣的事实是,浏览器也会将async函数进行实现:浏览器像Babel同样利用generatorspromises转换async异步

那么到底发生了些什么?

有些时候,为了理解一些东西如何运做,最好的方法就是本身动手作。async

好比咱们有一段使用了async函数的代码片断,咱们如何利用generatorspromises去重写它呢?函数

这是咱们的async函数:

函数体中依次执行三个异步任务,每一个任务依赖前一个任务的完成。最后,函数返回最后一个任务的结果。

如何使用generators重写

生成器的功能是:能够退出并再次进入。让咱们快速回顾一下它的工做方式,如下是一个简单的generator函数:

这个生成器函数gen拥有一些有趣的特性(从MDN摘取):

  1. 当一个generator函数被调用,函数体内代码并不当即执行。它返回一个遵循了迭代器协议迭代器对象:它有next方法
  2. 执行gen函数体内代码的惟一方法就是在返回的迭代器对象上调用next方法。每一次调用next,函数体内代码就执行到一个yield表达式处,这个表达式的右值赋值给迭代器
  3. next方法也能够接受参数,使用参数调用将会用参数值替换上一条yield表达式的左值,而后执行并返回当前yield表达式的右值
const a = yiled foo();
// | |
// | |
// 左值 右值
复制代码

请反复理解上述步骤或者参考MDN文档

这些特性如何帮助咱们?

到目前为止,你可能会疑惑,generator函数如何表达本文意图?

咱们须要创建一个异步工做流模型:即咱们须要进行下一步时,必须等待特定任务结束。

可是到目前为止,咱们讨论的东西都是同步的。怎么办?

译者注:上文的yield表达式后面全是同步值

关键点是生成器函数能够对promises进行yield

一个generator函数能够对promise进行yield,而且它的迭代器能够被控制中止并等待promise最终resolvereject并对他们决议的值进行下一步处理。这种构造一个可yield promises的迭代器的模式能够知足咱们的需求:

Notice how this generator function resembles our async function!

目前为止咱们只进行到一半。咱们须要一个执行函数体内容的方法,咱们须要一个能够控制generator函数迭代器的函数,它可以中止并等待每个yield promise决议的结果。听上去很复杂,可是实现起来仍是很简单的 :

A function that executes a generator function. (Only for explanation, do not use it !)

如今咱们能够像下面同样去使用runner函数执行咱们的生成器函数init

Use `runner` to execute the body of `init`.

就这么简单!runner函数和init函数的组合使用达到了原生async函数的效果。

请切记这个runner函数仅仅是为了讲解本文意图而作的演示代码,它不适合实际开发场景,若是你须要一个合适的实现,你能够在这里找找。

总结

咱们开始于一个async函数,而后利用generatorspromises去实现相同功能:

深刻实践

  • 本文伊始,咱们看到BabelES2017async函数转换以后,如何利用ES2016generatorspromises去实现。你能够回顾一下以前转换以后的_asyncToGenerator函数,比较咱们的runner函数就会发现二者很类似。实际上,_asyncToGenerator函数是咱们这里极其简单的runner函数万无一失的版本

  • 若是你还有兴趣,你能够进行下一步研究,即把async函数转换成没有generatorsES2015版本代码。这样你可能须要去模拟generators自己(参见regenerator project

我但愿经过这篇文章拨开async函数的迷雾,他提供了简单的语法,减小了代码噪声。async函数的提议是这样描述的:

The introduction of Promises and Generators in ECMAScript presents an opportunity to dramatically improve the language-level model for writing asynchronous code in ECMAScript.

感谢Akos, Alisa以及Kristian为完善这篇文章所提供的反馈。

相关文章
相关标签/搜索