JavaScript 异步编程--Generator函数、async、await
JavaScript 异步编程–Generator函数
Generator(生成器)是ES6标准引入的新的数据类型,其最大的特点就是可以交出函数的执行的控制权,即:通过yield关键字标明需要暂停的语句,执行时遇到yield语句则返回该语句执行结果,等到调用next函数时(也就是说可以通过控制调用next函数的时机达到控制generator执行的目的)重新回到暂停的地方往下执行,直至generator执行结束。
基本结构
以下是一个典型的generator函数的示例,以”*”标明为generator。
function* gen(x){ var y = yield x + 2; console.log(y); // undefine var yy = yield x + 3; console.log(yy); // 6 return 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 something func2(function (res2) { // do something func3(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博客如需转载请紧急联系作者

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Javascript 开启浏览器全屏模式
通常在某些情况下,我们需要让浏览器开启全屏模式,以便获得更好的视觉体验,先看下全屏模式简单的几个API。 浏览器默认绑定 非全屏模式下, document的F11按键绑定开启全屏模式 全屏模式下, document的esc和F11 按键绑定关闭全屏模式 屏幕全屏模式改变事件 当成功进入全屏模式后, document会收到一个fullscreenchange 事件。 当退出全屏状态后, document又会收到fullscreenchange 事件。 varscreenChange='webkitfullscreenchange'||'mozfullscreenchange'||'fullscreenchange' 判断当前是否处于全屏状态 非标准: varisFullScreen=document.fullScreen||document.mozFullScreen||document.webkitIsFullScreen; 标准: varisFullScreen=document.fullscreenElement||document.mozFullScreenElement||do...
- 下一篇
Javascript:npm模块安装机制简介
正因为有了npm,我们只要一行命令,就能安装别人写好的模块 。 $ npm install 1 本文介绍 npm 模块安装机制的细节,以及如何解决安装速度慢的问题。 一、从npm install说起 npm install 命令用来安装模块到node_modules目录。 $ npm install <packageName> 1 安装之前,npm install会先检查,node_modules目录之中是否已经存在指定模块。如果存在,就不再重新安装了,即使远程仓库已经有了一个新版本,也是如此。 如果你希望,一个模块不管是否安装过,npm 都要强制重新安装,可以使用-f或–force参数。 $ npm install <packageName> --force 1 二、npm update 如果想更新已安装模块,就要用到npm update命令。 $ npm update <packageName> 1 它会先到远程仓库查询最新版本,然后查询本地版本。如果本地版本不存在,或者远程版本较新,就会安装。 三、registrynpm update命令怎么知...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7设置SWAP分区,小内存服务器的救世主
- Mario游戏-低调大师作品
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- 2048小游戏-低调大师作品
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题