Generator

生成器是es6原生提供的异步编程方案,其语法行为和传统函数完全不同,阮大的《ECMAScript 6 入门》一书中对生成器有比较详尽的介绍,还有一些其他的文章可以参考,比如:

  • 《ECMAScript 6 入门:generator》

  • 深入浅出ES6(三):生成器 Generators

  • 深入浅出ES6(十一):生成器 Generators,续篇

本文主要是通过一些代码示例来记录和总结生成器的用法。

yield 和 next

yieldnext在生成器中扮演着非常重要的角色,前者是一个操作符,后者是生成器上的一个函数。

他们具有以下特性:

  1. 需要调用generator的next函数,生成器中的语句才开始执行;

  2. next函数在生成器之外调用,意味着可以在生成器之外控制其内部操作的执行过程;

  3. 当生成器执行到yield操作符就立即执行yield之后的语句并暂停,不敢妄言内部原理,姑且感性地比作savepoint;

  4. 当再次调用生成器的next函数时,生成器从上次发生yield'savepoint'继续执行,直到再次遇到yield,或者遇到是return或者throw生成器就退出;

  5. next的返回值是一个形如{done:false, value:x}的对象,每次调用next都会使生成器继续执行,对于next的返回值有如下规律:

    • 如果再次遇到yieldnext返回值中的value属性是紧接在这条yield之后的语句执行之后的返回值;

    • 如果遇到的是return,那么返回对象done=truevalue则是return的返回值;

    • 其他情况下,返回对象{done:false, value:undefined};

  6. next的输入参数在上一次发生yield的地方返回,所以第一次调用next传入的参数是“然并卵”,next是在生成器之外调用的,所以这个机制使得我们有能力控制生成器内部的行为。

以上说了很多,先看一个用生成器实现的一个无限斐波那契数列,可以无限的调用next函数,他永远不会新视觉返回done=true


const f = function* fibonacci() {
let [a, b] = [0, 1]; for (;;) {
yield a;
[a, b] = [b, a + b];
}
}(); //执行三次,得到三个对象,其value值分别是0,1,1
for (let i of Array(3).keys()) {
console.log(f.next());
}

接下来通过一段代码看看next和yield在传值和返回值上的情况,如下:


const iter = function* gen() {
console.log(`yield ${(yield 'a' + 0)}`);
console.log(`yield ${(yield 'b' + 1)}`);
return 'c' + 2;
}(); console.log(`next:${iter.next(0).value}`); //输出 next:a0
console.log(`next:${iter.next(1).value}`); //输出 yield 1 next:b1
console.log(`next:${iter.next(2).value}`); //输出 yield 2 next:c2

对以上代码的输出分析如下:

  1. 第一个next触发生成器执行到第一个yield,并立即执行'a' + 0 = 'a0'a0作为这次next的返回值;

  2. 第二个带参数为1next触发生成器继续执行,此时第一个yield才返回1,然后执行到第二个yield并立即立即这条yield后面的'b' + 1 = 'b1'b1作为这次next的返回;

  3. 第三个next执行以此类推……

异步编程方案

在同步编程模型中,每个函数总是有序依次地执行,一般上一个函数执行的结果往往是下一个函数的入参,那么在javascript中如何让下一个异步操作等待上一个异步执行得到结果之后再执行呢?

我们现在已经有了生成器并且知道next可以触发生成器执行到yield操作处,而且生成器会在遇到yield时立即执行后面的语句并暂停,那么如果yield后面是一个异步操作,而异步操作获取到结果之后再调用next不就实现了等待新视觉影院的效果么?


function asyncfuc(v) {
setTimeout(function() {
let r = v + 20;
console.log(r);
g.next(r); //把异步函数执行得到的结果传出并触发下一个yield
}, 500);
} let g = function* gen() {
let v1 = yield asyncfuc(0);
let v2 = yield asyncfuc(v1); //上一个异步调用的结果作为下一个异步调用的入参
return v2;
}(); g.next();

异步操作执行链

有了前文的基础我们可以实现一个用来执行多个异步操作的函数,定义一个run(...functions)方法依次执行传入的函数,如下:


//这个方法用来模拟一个异步调用
function delay(time, callback) {
setTimeout(function () {
callback(`slept for ${time}`);
}, time);
} function run(...functions) {
//构造一个生成器循环执行传入的方法
var generator = function* sync(resume, functions) {
let result;
for (var func of functions) {
result = yield func(result, resume); //前一个方法执行的结果作为下一个方法的入参
}
return result;
}(resume, functions); //提供一个方法用于推进生成器执行。
function resume(callbackValue) {
generator.next(callbackValue);
}
generator.next(); //触发生成器立即执行第一个方法
} //模拟异步方法调用, 斐波那契数列
function d(result, resume) {
delay(1000, (msg) => {
let value = result;
if (value) {
[value.a, value.b] = [value.b, value.a + value.b];
} else {
value = { a: 0, b: 1 };
}
console.log(value.a);
resume(value);
});
return result;
} run(d, d, d); //顺序执行异步方法

以上实现有个值得注意的地方是通过一个叫做的resume的函数来推进生成器,它要求业务处理函数必须带上一个resume并在获得异步结果之后把结果作为参数并执行resume(value)(如同上面的d(result, resume)函数),由于resume不是一个标准的api,因此这个方案具有比较大的侵入性。

ES6 异步编程之一:Generator的更多相关文章

  1. 转: ES6异步编程:Generator 函数的含义与用法

    转: ES6异步编程:Generator 函数的含义与用法 异步编程对 JavaScript 语言太重要.JavaScript 只有一根线程,如果没有异步编程,根本没法用,非卡死不可. 以前,异步编程 ...

  2. 转: ES6异步编程: co函数库的含义与用法

    转: ES6异步编程: co函数库的含义与用法 co 函数库是著名程序员 TJ Holowaychuk 于2013年6月发布的一个小工具,用于 Generator 函数的自动执行. 比如,有一个 Ge ...

  3. 转: ES6异步编程:Thunk函数的含义与用法

    转: ES6异步编程:Thunk函数的含义与用法 参数的求值策略 Thunk函数早在上个世纪60年代就诞生了. 那时,编程语言刚刚起步,计算机学家还在研究,编译器怎么写比较好.一个争论的焦点是&quo ...

  4. 深入解析js异步编程利器Generator

    我们在编写Nodejs程序时,经常会用到回调函数,在一个操作执行完成之后对返回的数据进行处理,我简单的理解它为异步编程. 如果操作很多,那么回调的嵌套就会必不可少,那么如果操作非常多,那么回调的嵌套就 ...

  5. JavaScript异步编程:Generator与Async

    从Promise开始,JavaScript就在引入新功能,来帮助更简单的方法来处理异步编程,帮助我们远离回调地狱. Promise是下边要讲的Generator/yield与async/await的基 ...

  6. es6异步编程 Promise 讲解 --------各个优点缺点总结

    //引入模块 let fs=require('fs'); //异步读文件方法,但是同步执行 function read(url) { //new Promise 需要传入一个executor 执行器 ...

  7. es6异步编程

    https://blog.csdn.net/tcy83/article/details/80274772 等一系列文章

  8. es6 generator函数的异步编程

    es6 generator函数,我们都知道asycn和await是generator函数的语法糖,那么genertaor怎么样才能实现asycn和await的功能呢? 1.thunk函数    将函数 ...

  9. ES6/7 异步编程学习笔记

    前言 在ES6的异步函数出现之前,Js实现异步编程只有settimeout.事件监听.回调函数等几种方法 settTmeout 这种方法常用于定时器与动画的功能,因为其本质上其实是浏览器的WebAPI ...

随机推荐

  1. Selenium3+webdriver学习笔记2(常用元素定位方式,定位单个元素共8种,总共有23种)

    #!/usr/bin/env python# -*- coding:utf-8 -*- from selenium import webdriver import time,os # about:ad ...

  2. JavaScript对象属性

    JavaScript对象的属性有两类:数据属性和访问器属性 数据属性 数据属性有四个特性,分别为: [[value]]属性的值 [[writeable]]属性是否可以修改 [[enumerable]] ...

  3. 三维GIS-室内寻径功能实现

    期末,要交一个大作业,正巧之前跑国图借书的时候,晕头转向的,国图内居然没有导航!!!借这个机会做一个室内导航的demo,只是半成品,还需要加入室内定位,匹配一下坐标才能在实际中使用. demo:利用蜂 ...

  4. Html.Action Html.RenderAction Html.Partial Html.RenderPartial Url.Action Html.ActionLink 大括号和小括号区别

    在查阅了一些资料后,结论如下: Action 是以mvchtmlstring的方式返回一个结果,RenderAction 无返回值,速度上action慢于RenderAction partial和Re ...

  5. 关于 NetBackup 应答文件(/tmp/NBInstallAnswer.conf)

    关于 NetBackup 应答文件 在 UNIX 和 Linux 安装和升级期间使用 NetBackup 应答文件 (/tmp/NBInstallAnswer.conf),以便: 覆盖某些默认值. 避 ...

  6. PSNR

    PSNR,峰值信噪比,通常用来评价一幅图像压缩后和原图像相比质量的好坏,当然,压缩后图像一定会比原图像质量差的,所以就用这样一个评价指标来规定标准了.PSNR越高,压缩后失真越小.这里主要定义了两个值 ...

  7. DROP GROUP - 删除一个用户组

    SYNOPSIS DROP GROUP name DESCRIPTION 描述 DROP GROUP 从数据库中删除指定的组.组中的用户不被删除. 组中的用户不被删除. PARAMETERS 参数 n ...

  8. 【转】MFC消息映射详解(整理转载)

    消息:主要指由用户操作而向应用程序发出的信息,也包括操作系统内部产生的消息.例如,单击鼠标左按钮,windows将产WM_LBUTTONDOWN消息,而释放鼠标左按钮将产生WM_LBUTTONUP消息 ...

  9. ANSI C 与 K&R C

    C语言由Dennis M.Ritchie在1973年设计和实现.从那以后使用者逐渐增加.到1978年Ritchie和Bell实验室的另一位程序专家Kernighan合写了著名的<TheC Pro ...

  10. 【贪心 计数】bzoj2006: [NOI2010]超级钢琴

    这么经典的贪心我怎么现在才做啊…… Description 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的 音乐. 这架超级钢琴可以弹奏出n个 ...