by William Gottschalk
威廉·戈特沙克(William Gottschalk)
使用Promise,Generators和Coroutines编写现代异步Javascript (Write Modern Asynchronous Javascript using Promises, Generators, and Coroutines)
Over the years, “Callback Hell” is often cited as one of the most hated design patterns in Javascript for managing concurrency. Just in case you’ve forgotten what that looks like, here is an example of a varying and processing a transaction in Express:
多年来,“回调地狱”经常被称为Javascript中最令人讨厌的并发管理设计模式之一。 万一您忘记了它的外观,下面是在Express中更改和处理事务的示例:
应许是要拯救我们的…… (Promises were supposed to save us…)
I was told that promises would allow us Javascript developers to write asynchronous code as if it were synchronous by wrapping our async functions in a special object. In order to access the value of the Promise, we call either .then or .catch on the Promise object. But what happens when we try to refactor the above example using Promises?
有人告诉我,通过将我们的异步函数包装在一个特殊的对象中,诺言将使我们的Javascript开发人员能够像异步代码一样编写异步代码。 为了访问的承诺的价值,我们称之为要么。然后或.catch无极对象。 但是,当我们尝试使用Promises重构上述示例时,会发生什么?
Since each function inside of the callback is scoped, we cannot access the user object inside of the second .then callback.
由于回调内部的每个函数都具有作用域,因此我们无法在第二个.then回调内部访问用户对象。
So after a little digging, I couldn’t find an elegant solution, but I did find a frustrating one:
因此,经过一番挖掘,我找不到一个优雅的解决方案,但确实找到了一个令人沮丧的解决方案:
Just indent your promises so that they have proper scoping.
只是缩进您的诺言,以便他们有适当的范围。
Indent my promises!? So its back to the Pyramid of Doom now?
兑现我的诺言!? 那么现在回到厄运金字塔了吗?
I would argue that the nested callback version looks cleaner and is easier to reason about than the nested promise version.
我认为嵌套的回调版本看起来比嵌套的Promise版本更干净,并且更易于推理。
异步等待将拯救我们! (Async Await Will Save Us!)
The async and await keywords will allow us to write our javascript code as though it is synchronous. Here is code written with those keywords coming in ES7:
async和await关键字将使我们能够像同步代码一样编写JavaScript代码。 这是用ES7中的那些关键字编写的代码:
Unfortunately the majority of ES7 features including async/await have not been natively implemented and therefore, require the use of a transpiler. However, you can write code that looks exactly like the code above using ES6 features that have been implemented in most modern browsers as well as Node version 4+.
不幸的是,大多数ES7功能(包括异步 / 等待)尚未本地实现,因此需要使用编译器。 但是,您可以使用大多数现代浏览器以及Node 4+版本中实现的ES6功能编写与上述代码完全相似的代码。
动态二重奏:生成器和协程 (The Dynamic Duo: Generators and Coroutines)
Generators are a great metaprogramming tool. They can be used for things like lazy evaluation, iterating over memory intensive data sets and on-demand data processing from multiple data sources using a library like RxJs.
生成器是一个很棒的元编程工具。 它们可用于诸如延迟评估,遍历内存密集型数据集以及使用RxJ之类的库从多个数据源进行按需数据处理的事情。
However, we wouldn’t want to use generators alone in production code because they forces us to reason about a process over time. And each time we call next, we jump back to our generator like a GOTO statement.
但是,我们不想在生产代码中单独使用生成器,因为它们会迫使我们随着时间的推移对某个过程进行推理。 每次我们调用下一步时,我们都会像GOTO语句一样跳回到生成器。
Coroutines understand this and remedy this situation by wrapping a generator and abstracting away all of the complexity.
协程了解这一点,并通过包装生成器并抽象化所有复杂性来纠正这种情况。
使用协程的ES6版本 (The ES6 version using Coroutine)
Coroutines allow us to yield our asynchronous functions one line at a time, making our code look synchronous.
协程使我们可以一次产生一行异步函数,使我们的代码看起来是同步的。
It’s important to note that I am using the Co library. Co’s coroutine will execute the generator immediately where as Bluebird’s coroutine will return a function that you must invoke to run the generator.
请务必注意,我正在使用Co库。 Co的协程将立即执行生成器,而Bluebird的协程将返回您必须调用以运行生成器的函数。
Let’s establish some basic rules to using coroutines:
让我们建立一些使用协程的基本规则:
Any function to the right of a yield must return a Promise.
收益率右边的任何函数都必须返回Promise。
If you want to execute your code now, use co.
如果要立即执行代码,请使用co 。
If you want to execute your code later, use co.wrap.
如果要稍后执行代码,请使用co.wrap 。
Make sure to chain a .catch at the end of your coroutine to handle errors. Otherwise, you should wrap your code in a try/catch block.
确保在协程的末尾链接.catch来处理错误。 否则,您应该将代码包装在try / catch块中。
Bluebird’s Promise.coroutine is the equivalent to Co’s co.wrap and not the co function on it’s own.
蓝鸟的Promise.coroutine等同于Co的co.wrap,而不是其本身的co函数。
如果要同时运行多个进程怎么办? (What if I want to run multiple processes concurrently?)
You can either use objects or arrays with the yield keyword and then destructure the result.
您可以将对象或数组与yield关键字一起使用,然后对结果进行解构。
您今天可以使用的库: (Libraries that you can use today:)
Promise.coroutine | bluebirdBluebird is a fully featured JavaScript promises library with unmatched performance.bluebirdjs.comcogenerator async control flow goodnesswww.npmjs.comBabel · The compiler for writing next generation JavaScriptThe compiler for writing next generation JavaScriptbabeljs.ioasyncawaitasync/await for node.jswww.npmjs.com
Promise。协程| bluebird Bluebird是功能齐全JavaScript Promise库,具有无与伦比的性能。 bluebirdjs.com 协同 生成器异步控制流优度 www.npmjs.com Babel· 编写下一代JavaScript 的编译器编写 下一代JavaScript 的编译器 babeljs.io asyncawait node.js的async / await