JavaScript 异步编程–Generator函数

Generator(生成器)是ES6标准引入的新的数据类型,其最大的特点就是可以交出函数的执行的控制权,即:通过yield关键字标明需要暂停的语句,执行时遇到yield语句则返回该语句执行结果,等到调用next函数时(也就是说可以通过控制调用next函数的时机达到控制generator执行的目的)重新回到暂停的地方往下执行,直至generator执行结束。

基本结构

以下是一个典型的generator函数的示例,以”*”标明为generator。

function* gen(x){var y = yield x + 2;console.log(y);         // undefinevar yy = yield x + 3;console.log(yy);        // 6return y;               // 没啥用
}var g = gen(1);
var r1 = g.next();
console.log(r1);     // { value: 3, done: false }
var r2 = g.next();
console.log(r2);     // { value: 4, done: false }
var r3 = g.next(6);
console.log(r3);     // { value: undefined, done: true }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

上述代码中,调用gen函数,会返回一个内部指针(即遍历器)g,这是Generator函数和一般函数不同的地方,调用它不会返回结果,而是一个指针对象。调用指针g的next方法,会移动内部指针,指向第一个遇到的yield语句,上例就是执行到x+2为止。换言之,next方法的作用是分阶段执行Generator函数。每次调用next方法,会返回一个对象{value: any, done: boolean},表示当前阶段的信息,其中value属性是yield语句后面表达式的值;done属性是一个布尔值,表示Generator函数是否执行完毕,即是否还有下一个阶段。next方法输入参数即为yield语句的值,因此生成器gen中y为第二次调用next的输入参数”undefine”,yy为第三次调用next的输入参数6。 
总结:

  • generator返回遍历器,可遍历所有yield
  • yield将生成器内部代码分割成n段,通过调用next方法一段一段执行
  • next方法返回的value属性向外输出数据,next方法通过实参向生成器内部输入数据

思考

如果yield标记的语句是个异步执行的函数func,然后在func回调中调用next,则实现了等待func异步执行的效果—–“func要做的事做完了,才会往下走”,这样就避免了多重回调嵌套(callback hell,回调地狱, 如下所示)

func1(function (res) {// do somethingfunc2(function (res2) {// do somethingfunc3(function (res3) {// do something})})
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Thunk函数

什么是thunk函数?详见Thunk 函数的含义和用法 
简单理解:thunk函数利用闭包可以缓存状态的特性,先传参数,再执行函数,这样就将函数调用过程分成了两步。以下thunkify函数可将普通异步函数转化为thunk函数。

function thunkify(fn) {assert('function' == typeof fn, 'function required');return function () {// arguments为异步函数的参数(不包含回调函数参数)var args = new Array(arguments.length);var ctx = this;for (var i = 0; i < args.length; ++i) {args[i] = arguments[i];}// done为异步函数的回调函数(callback)return function (done) {var called;args.push(function () {if (called) return;called = true;done.apply(null, arguments);});try {// 到这里,异步函数才真正被调用fn.apply(ctx, args);} catch (err) {done(err);}}}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

Generator执行控制

thunk函数有什么用呢?其一个典型应用就是用于控制generator的执行,见如下示例是为了实现多个文件的顺序读取,实现了同步写法,避免回调嵌套。

const fs = require('fs');
const readFileThunk = thunkify(fs.readFile);var generator = function* () {for (var i = 0; i < arguments.length; i++) {console.log('file: %s', arguments[i]);// yield 返回thunkify最内部的 function (done){}  函数,此处传入了readFile函数参数,但并没有执行var r1 = yield readFileThunk(arguments[i], 'utf8');console.log('r1: %s', r1);}
}function rungenerator(generator) {//文件名称var args = [];for (var i = 1; i < arguments.length; i++) {args.push(arguments[i]);}//生成generator实例var gen = generator.apply(null, args);function done(err, data) {//执行跳到 generator中去var result = gen.next(data);if (result.done) { return; }// 此处才是真正的调用readFile函数开始读取文件内容,done作为回调, 文件读取完成后,执行gen.next(),// 告诉generator继续执行,并通过yield返回下一个thunk函数,开始读取下一个文件,从而达到顺序执行的效果result.value(done);}next();
}
rungenerator(generator, '123.txt', '1234.txt', 'he.txt')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

上述代码中,rungenerator是一个执行generator的函数,具有通用性,封装下就成了co库—-generator函数自动执行的解决方案。

var fs = require('fs');
var co = require('co');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);co(function*(){var files=['./text1.txt', './text2.txt', './text3.txt'];var p1 = yield readFile(files[0]);console.log(files[0] + ' ->' + p1);var p2 = yield readFile(files[1]);console.log(files[1] + ' ->' + p2);var p3 = yield readFile(files[2]);console.log(files[2] + ' ->' + p3);return 'done';
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

看起来舒服多了。。。

async和await

async和await是ES7中的新语法,实际上是generator函数的语法糖 
Nodejs最新版已经支持了,浏览器不支持的,可以用Babel转下。

var fs = require('fs');var readFile = function (fileName){return new Promise(function (resolve, reject){fs.readFile(fileName, function(error, data){if (error){reject(error);}else {resolve(data);}});});
};var asyncReadFile = async function (){var f1 = await readFile('./text1.txt');var f2 = await readFile('./text2.txt');console.log(f1.toString());console.log(f2.toString());
};asyncReadFile();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

致谢

主要学习了nullcc的博客《深入解析Javascript异步编程》、无脑的博客《node的 thunkify模块说明》、阮一峰老师的博客《Thunk 函数的含义和用法》,由衷地感谢以上作者!!!!!

原文发布时间:2018-6-19

原文作者:阳光七十米

本文来源csdn博客如需转载请紧急联系作者

JavaScript 异步编程--Generator函数、async、await相关推荐

  1. JS 异步编程终极解决方案 async/await 的使用手册

    前言 async functions 和 await 关键字是最近添加到JavaScript语言里面的.它们是ECMAScript 2017 JavaScript版的一部分.简单来说,它们是基于pro ...

  2. JavaScript异步编程【下】 -- Generator、Async/await

    文章内容输出来源:拉勾教育 大前端高薪训练营 前言 在JavaScript异步编程[上]和 JavaScript异步编程[中]中,我们已经讲到了处理异步编程的两种方法:回调函数 和 Promise. ...

  3. JavaScript异步编程【上】 -- 同步和异步、事件循环(EventLoop)、微任务和宏任务、回调函数

    文章内容输出来源:拉勾教育 大前端高薪训练营 前言 在我们学习JavaScript中,我们知道,JavaScript的执行环境是单线程的.所谓单线程是指一次只能完成一个任务,如果有多个任务,就必须排队 ...

  4. 【学习笔记】Part1·JavaScript·深度剖析-函数式编程与 JS 异步编程、手写 Promise(二、JavaScript 异步编程)

    [学习笔记]Part1·JavaScript·深度剖析-函数式编程与 JS 异步编程.手写 Promise(课前准备) [学习笔记]Part1·JavaScript·深度剖析-函数式编程与 JS 异步 ...

  5. 写给初学者的JavaScript异步编程和背后思想

    导读: 对于接触JavaScript这门编程语言没有多久的本菜鸡而言,在相当长的一段时间内,我都完全无法理解这门语言中的异步编程,不明白什么叫异步编程以及为什么需要异步编程.为什么顺序执行程序就不行了 ...

  6. async function_理解 Iterator, Generator 和 Async/Await

    戳蓝字「前端技术优选」关注我们哦! 这里重点理解他们三者分别是什么,有什么区别,以及分别适用什么场景 Iterator Iterator是最简单最好理解的,在很久之前我写过一篇文章 循环的秘密 里面讨 ...

  7. 网页javascript加载不出_写给初学者的JavaScript异步编程和背后思想

    导读:对于接触JavaScript这门编程语言没有多久的本菜鸡而言,在相当长的一段时间内,我都完全无法理解这门语言中的异步编程,不明白什么叫异步编程以及为什么需要异步编程.为什么顺序执行程序就不行了呢 ...

  8. JavaScript异步编程原理

    众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着. ...

  9. Javascript异步编程之一异步原理

    本系列的例子主要针对node.js环境,但浏览器端的原理应该也是类似的. 本人也是Javascript新手,把自己这段时间学习积累的要点总结下来,希望可以对同样在学习Javascript/node.j ...

最新文章

  1. 一次 HashSet 所引起的并发问题
  2. iOS Bluetooth(蓝牙)
  3. Ubuntu安装搜狗输入法Linux版
  4. 初识莫队——小Z的袜子
  5. 玩游戏也能学Python?!论Python的正确打开方式
  6. [react] 在react中怎样改变组件状态,以及状态改变的过程是什么?
  7. hadoopsdk使用_hadoop部署使用问题及解决
  8. java微信公众号支付示例
  9. day20: zip压缩工具及打包工具介绍
  10. 能否利用Hadoop搭建完整的云计算平台
  11. 学习笔记(二)之字符常量和字符串常量
  12. 均匀B样条和准均匀B样条
  13. 物理学经济学java周易_八竿子打不着?——物理学和经济学的相似相通之处
  14. 路由及路由器工作原理深入解析3:路由与端口
  15. 【随笔记】linux usb gadget ncm wrong ndp sign 问题修复
  16. 跑步戴哪款无线耳机好,适合跑步小白的无线耳机推荐
  17. 浮生事,何必念念不忘
  18. Ubuntu下开机禁用笔记本触摸板
  19. 现代A200(MoboDA3360)玩家宝典
  20. 联想x3850x6从u盘引导,联想ThinkPad X280 BIOS设置u盘启动教程

热门文章

  1. Uber将整体式API拆分为微服务
  2. Storm构建分布式实时处理应用初探(转)
  3. 浅谈Android五大布局——LinearLayout、FrameLayout和AbsoulteLa
  4. 建筑物占据的网格数目的确定(三)
  5. RadGrid使用技巧:从RadGrid获取绑定的值
  6. 51js 的json编辑器
  7. C/C++ 误区:fflush(stdin)
  8. (灌水)如何限制一个WinForm应用程序只能在一个进程运行
  9. python爬取正确但不出文件_python爬取糗事百科,该如何正确保存到本地文件夹?报错-问答-阿里云开发者社区-阿里云...
  10. linux添加域名证书,在Linux服务器上手动安装免费的Let's Encrypt域名证书 - 乐道主机...