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); // 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相关推荐
- JS 异步编程终极解决方案 async/await 的使用手册
前言 async functions 和 await 关键字是最近添加到JavaScript语言里面的.它们是ECMAScript 2017 JavaScript版的一部分.简单来说,它们是基于pro ...
- JavaScript异步编程【下】 -- Generator、Async/await
文章内容输出来源:拉勾教育 大前端高薪训练营 前言 在JavaScript异步编程[上]和 JavaScript异步编程[中]中,我们已经讲到了处理异步编程的两种方法:回调函数 和 Promise. ...
- JavaScript异步编程【上】 -- 同步和异步、事件循环(EventLoop)、微任务和宏任务、回调函数
文章内容输出来源:拉勾教育 大前端高薪训练营 前言 在我们学习JavaScript中,我们知道,JavaScript的执行环境是单线程的.所谓单线程是指一次只能完成一个任务,如果有多个任务,就必须排队 ...
- 【学习笔记】Part1·JavaScript·深度剖析-函数式编程与 JS 异步编程、手写 Promise(二、JavaScript 异步编程)
[学习笔记]Part1·JavaScript·深度剖析-函数式编程与 JS 异步编程.手写 Promise(课前准备) [学习笔记]Part1·JavaScript·深度剖析-函数式编程与 JS 异步 ...
- 写给初学者的JavaScript异步编程和背后思想
导读: 对于接触JavaScript这门编程语言没有多久的本菜鸡而言,在相当长的一段时间内,我都完全无法理解这门语言中的异步编程,不明白什么叫异步编程以及为什么需要异步编程.为什么顺序执行程序就不行了 ...
- async function_理解 Iterator, Generator 和 Async/Await
戳蓝字「前端技术优选」关注我们哦! 这里重点理解他们三者分别是什么,有什么区别,以及分别适用什么场景 Iterator Iterator是最简单最好理解的,在很久之前我写过一篇文章 循环的秘密 里面讨 ...
- 网页javascript加载不出_写给初学者的JavaScript异步编程和背后思想
导读:对于接触JavaScript这门编程语言没有多久的本菜鸡而言,在相当长的一段时间内,我都完全无法理解这门语言中的异步编程,不明白什么叫异步编程以及为什么需要异步编程.为什么顺序执行程序就不行了呢 ...
- JavaScript异步编程原理
众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着. ...
- Javascript异步编程之一异步原理
本系列的例子主要针对node.js环境,但浏览器端的原理应该也是类似的. 本人也是Javascript新手,把自己这段时间学习积累的要点总结下来,希望可以对同样在学习Javascript/node.j ...
最新文章
- 一次 HashSet 所引起的并发问题
- iOS Bluetooth(蓝牙)
- Ubuntu安装搜狗输入法Linux版
- 初识莫队——小Z的袜子
- 玩游戏也能学Python?!论Python的正确打开方式
- [react] 在react中怎样改变组件状态,以及状态改变的过程是什么?
- hadoopsdk使用_hadoop部署使用问题及解决
- java微信公众号支付示例
- day20: zip压缩工具及打包工具介绍
- 能否利用Hadoop搭建完整的云计算平台
- 学习笔记(二)之字符常量和字符串常量
- 均匀B样条和准均匀B样条
- 物理学经济学java周易_八竿子打不着?——物理学和经济学的相似相通之处
- 路由及路由器工作原理深入解析3:路由与端口
- 【随笔记】linux usb gadget ncm wrong ndp sign 问题修复
- 跑步戴哪款无线耳机好,适合跑步小白的无线耳机推荐
- 浮生事,何必念念不忘
- Ubuntu下开机禁用笔记本触摸板
- 现代A200(MoboDA3360)玩家宝典
- 联想x3850x6从u盘引导,联想ThinkPad X280 BIOS设置u盘启动教程
热门文章
- Uber将整体式API拆分为微服务
- Storm构建分布式实时处理应用初探(转)
- 浅谈Android五大布局——LinearLayout、FrameLayout和AbsoulteLa
- 建筑物占据的网格数目的确定(三)
- RadGrid使用技巧:从RadGrid获取绑定的值
- 51js 的json编辑器
- C/C++ 误区:fflush(stdin)
- (灌水)如何限制一个WinForm应用程序只能在一个进程运行
- python爬取正确但不出文件_python爬取糗事百科,该如何正确保存到本地文件夹?报错-问答-阿里云开发者社区-阿里云...
- linux添加域名证书,在Linux服务器上手动安装免费的Let's Encrypt域名证书 - 乐道主机...