异步生成器

Nowadays we can write our asynchronous code in a synchronous way thanks to the async and await keywords. This makes it easier to read and understand. Recently I wondered, however, how the same effect could be achieved without using these keywords.

如今, 借助asyncawait关键字,我们可以以同步方式编写异步代码。 这样可以更容易阅读和理解。 但是,最近我想知道,不使用这些关键字怎么能达到相同的效果。

It turns out to be quite simple, since the behavior of async and await can easily be emulated using generators. Let’s have a look!

事实证明这很简单,因为可以使用生成器轻松模拟asyncawait的行为。 我们来看一下!

Go ahead, clone the repository and let’s get started.

继续,克隆存储库 ,让我们开始吧。

发电机 (Generators)

I am going to assume you have little to no experience with generators since, honestly, most of the time they aren’t particularly useful and you can easily manage without them. So don’t worry — we’ll start with a quick reminder.

我要假设您几乎没有发电机的经验,因为老实说,大多数时候它们并不是特别有用,并且没有它们,您可以轻松地进行管理。 因此,请放心-我们将快速提醒您。

Generators are objects created by generator functions — functions with an * (asterisk) next to their name.

生成器是由生成器函数创建的对象,这些函数的名称旁边带有* (星号)。

These generators have an amazing ability that lets us stop the execution of code — whenever we want — by using the keyword yield.

这些生成器具有惊人的功能,可让我们通过使用关键字yield随时停止代码的执行。

Consider this example:

考虑以下示例:

const generator = (function*() {// waiting for .next()const a = yield 5;// waiting for .next()console.log(a); // => 15
})();console.log(generator.next()); // => { done: false, value: 5 }
console.log(generator.next(15)); // => { done: true, value: undefined }

Given that these are absolute basics, I would recommend that, before you scroll any further, you read this article to get a grasp on what is really going on here.

鉴于这些都是绝对的基础知识,因此我建议您在进一步滚动之前,先阅读本文,以了解此处的实际情况。

If you feel like you have a strong understanding of the underlying ideas, we can move on.

如果您觉得自己对基本概念有深刻的了解,我们可以继续。

等一下等一下 (Hold on, await a minute)

Haven’t you ever wondered how await really works?

您是否从未想过等待是如何工作的?

Somehow it just waits for our promise to return a value and proceed with the execution. For me, that seems like something a generator would be able to do after a little tweaking.

它以某种方式只是等待我们的承诺返回值并继续执行。 对我来说,经过一些调整后,生成器似乎可以执行某些操作。

What we could do is just take every yielded value, put it into a promise, and then wait for the promise to be resolved. Afterwards, we just return it to the generator by calling generator.next(resolvedValue).

我们所能做的就是将每个产生的值取走,放入一个承诺中,然后等待该承诺被解决。 然后,我们通过调用generator.next(resolvedValue)将其返回给生成器。

Sounds like a plan. But first, let’s write some tests just to be sure that everything is working as expected.

听起来像是个计划。 但是首先,让我们编写一些测试,以确保一切都按预期进行。

What our asynq function should do:

我们的asynq函数应该做什么:

  • wait for asynchronous code before continuing the execution等待异步代码,然后继续执行
  • return a promise with the returned value from the function

    用函数返回的值返回一个承诺

  • make try/catch work on asynchronous code

    在异步代码上进行try / catch工作

Note: because we are using generators, our await becomes yield.

注意:因为我们使用的是生成器,所以等待变为yield

import { asynq } from '../src';describe('asynq core', () => {test('Waits for values (like await does)', () => {return asynq(function*() {const a = yield Promise.resolve('a');expect(a).toBe('a');});});test('Catches the errors', () => {return asynq(function*() {const err = new Error('Hello there');try {const a = yield Promise.resolve('a');expect(a).toBe('a');const b = yield Promise.resolve('b');expect(b).toBe('b');const c = yield Promise.reject(err);} catch (error) {expect(error).toBe(err);}const a = yield Promise.resolve(123);expect(a).toBe(123);});});test('Ends the function if the error is not captured', () => {const err = new Error('General Kenobi!');return asynq(function*() {const a = yield Promise.reject(err);const b = yield Promise.resolve('b');}).catch((error) => {expect(error).toBe(err);});});test('Returns a promise with the returned value', () => {return asynq(function*() {const value = yield Promise.resolve(5);expect(value).toBe(5);return value;}).then((value) => {expect(value).toBe(5);});});
});

Alright, great! Now we can talk about the implementation.

好吧,太好了! 现在我们可以谈谈实现。

Our asynq function takes as a parameter a function generator — by calling it, we create a generator.

我们的asynq函数将函数生成器作为参数-通过调用它,我们创建了一个生成器。

Just to be sure, we call isGeneratorLike which checks if the received value is an object and has methods next and throw.

可以肯定的是,我们调用isGeneratorLike ,它检查接收到的值是否是一个对象,并具有nextthrow方法。

Then, recursively, we consume each yield keyword by calling generator.next(ensuredValue). We wait for the returned promise to be settled, and then return its result back to the generator by repeating the whole process.

然后,递归地,我们消耗每个收益 通过调用generator.next(ensuredValue)关键字 我们等待返回的诺言得到解决,然后通过重复整个过程将其结果返回给生成器。

We must also attach the catch handler, so that, should the function throw an exception, we can catch it and return the exception inside the function by calling generator.throw(error).

我们还必须附加 捕获处理程序,因此,如果函数抛出异常,我们可以通过调用generator.throw(error)捕获它并在函数内部返回异常。

Now, any potential errors will be handled by catch. If there wasn’t a try/catch block in place, an error would simply stop the execution altogether — like any unhandled exception would — and our function would return a rejected promise.

现在,任何潜在的错误将由catch处理。 如果没有try / catch块,则发生错误 就像所有未处理的异常一样,将完全停止执行,并且我们的函数将返回被拒绝的Promise。

When the generator is done, we return the generator’s return value in a promise.

生成器完成后,我们以承诺方式返回生成器的返回值。

import { isGeneratorLike } from './utils';type GeneratorFactory = () => IterableIterator<any>;function asynq(generatorFactory: GeneratorFactory): Promise<any> {const generator = generatorFactory();if (!isGeneratorLike(generator)) {return Promise.reject(new Error('Provided function must return a generator.'),);}return (function resolve(result) {if (result.done) {return Promise.resolve(result.value);}return Promise.resolve(result.value).then((ensuredValue) => resolve(generator.next(ensuredValue))).catch((error) => resolve(generator.throw(error)));})(generator.next());
}

Now, having run our tests, we can see that everything is working as expected.

现在,运行我们的测试,我们可以看到一切都按预期进行。

结语 (Wrapping up)

While this implementation is probably not the one used inside the JavaScript engines, it sure feels good to be able to do something like this on our own.

尽管此实现可能不是JavaScript引擎内部使用的实现,但能够自己完成这样的事情确实感觉很好。

Feel free to go over the code again. The better your understanding of the underlying ideas, the more you will be able to appreciate the brilliance of the creators of the async and await keywords.

随意再次检查代码。 您对基本思想的理解越深入,您就越能体会到asyncawait关键字的创建者的才华。

Thank you very much for reading! I hope you found this article informative. I also hope it helped you see there is no magic involved in the async and await keywords, and that they can be easily replaced with generators.

非常感谢您的阅读! 希望您能从这篇文章中获得启发。 我也希望它能帮助您看到asyncawait关键字中没有魔术,并且可以轻松地将它们替换为生成器。

If you have any questions or comments, feel free to put them in the comments section below or send me a message.

如果您有任何问题或意见,请随时将其放在下面的评论部分或给我发送消息 。

Check out my social media!

看看我的社交媒体 !

Join my newsletter!

加入我的时事通讯 !

Originally published at www.mcieslar.com on August 6, 2018.

最初于2018年8月6日发布在www.mcieslar.com上。

翻译自: https://www.freecodecamp.org/news/how-to-implement-async-and-await-with-generators-11ab0859010f/

异步生成器

异步生成器_使用生成器实现异步并等待相关推荐

  1. python3异步编程_协程 Python异步编程(asyncio)

    协程(Coroutine) 也可以被称为微线程,是一种用户态内的上下文切换技术.简而言之,其实就是通过一个线程实现代码块相互切换执行. 直接上代码,例如: 同步编程 import time def f ...

  2. 深入理解python异步编程_深入理解Python异步编程

    1 什么是异步编程 1.1 阻塞程序未得到所需计算资源时被挂起的状态. 程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的. 常见的阻塞形式有:网络I/O阻塞.磁盘I/O ...

  3. java同步异步区别_同步请求和异步请求的区别

    同步请求和异步请求的区别 先解释一下同步和异步的概念 同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式. 异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的 ...

  4. python 正则表达式生成器_正则表达式生成器

    有生成正则表达式的工具吗? SQLSERVER2017 报表生成器里面怎么用正则表达式语一个字段 NAME 里面的值类似是这样 PB3*5平头螺丝,我想要筛选NAME字段sql server中对字段使 ...

  5. java的rest异步调用_使用AsyncRestTemplate进行异步调用

    背景: 最近项目中需要并发调用c++服务的http接口,问题是同时调用两个接口时,会发生严重阻塞,导致页面响应慢,还经常遇到接收数据超时,导致RestTemplate报出ReadTimeout错误,一 ...

  6. java excel异步导出_如何实现前端异步调用导出Excel?

    今天分享JBolt极速开发平台中使用的异步下载文件的解决方案. 需求场景: 列表查询界面有一个[导出Excel]按钮,需要点击按钮,弹出loading信息框,异步调用后台action,得到数据库数据后 ...

  7. java同步接口和异步接口_同步接口和异步接口

    定义 答案:来自网络搜索 同步调用:当一个支付请求被发送到支付渠道方,支付渠道会很快返回一个结果.但是这个结果,只是告诉你调用成功了,不是扣款成功,这叫同步调用; 异步调用:同步请求参数里面会有一个回 ...

  8. jquery.form 异步校验_利用jQuery.validate异步验证用户名是否存在

    HTML头部引用: HTML内容(部分): 用户名: JS代码部分: $(function() { //表单验证JS $("#form1").validate({ //出错时添加的 ...

  9. 异步解耦_如何使用异步生成器解耦业务逻辑

    异步解耦 Async generators are new in JavaScript. They are a remarkable extension. They provide a simple ...

最新文章

  1. 快速排序C实现(阿里巴巴 2012年全国校招笔试题)
  2. CCNP之IS-IS实验
  3. 微服务业务体系内对复用的深度探讨
  4. 反思读别人代码的思路
  5. [html] 页面布局时你使用最多的标签是什么?div吗?在什么情况下会使用到div?
  6. php多图片上传到数组,input type=file多图片上传 原生html传递的数组集合
  7. jquery实现的滚动新闻(多个实例代码)
  8. Linux查看版本信息及CPU内核、型号等
  9. Python打包成exe.文件教程分享
  10. caffe+报错︱深度学习参数调优杂记+caffe训练时的问题+dropout/batch Normalization
  11. MVX-Net: Multimodal VoxelNet for 3D Object Detection
  12. 止汗 咒语_如何使用咒语制作诗歌机器人
  13. 数据清洗整理基本操作(R:dplyr、tidyr、lubridate)
  14. C#开发测量程序-计算坐标方位角
  15. java poi导出PPT格式
  16. ERROR: torch-1.6.0+cu101-cp37-cp37m-win_amd64.whl is not a supported wheel on this platform.
  17. 项目管理小小知识点总结
  18. python实现c语言编译器安卓版_c语言编译器ide下载app-c语言编译器ide软件下载v1.5.1 安卓版-2265安卓网...
  19. js Javascript中调用对象内函数.(字符串函数名)
  20. Javascript的图片滚动浏览效果代码

热门文章

  1. 安卓模拟器安装过程记录 20200926
  2. linux-数据库篇-索引
  3. linux-vim-进入编辑模式的多种方法
  4. 精典排序,更新中。。。
  5. mongodb备份还原
  6. 解决maven项目中,缺少 maven dependencies
  7. linux中MySQL密码的恢复方法
  8. Windows Serivce服务实现过程和打包安装
  9. Redis集群方案及实现 - yfk的专栏 - 博客频道 - CSDN.NET
  10. 如何用sbt新建akka项目