异步编程系列教程:

  1. (翻译)异步编程之Promise(1)——初见魅力
  2. 异步编程之Promise(2):探究原理
  3. 异步编程之Promise(3):拓展进阶
  4. 异步编程之Generator(1)——领略魅力
  5. 异步编程之Generator(2)——剖析特性
  6. 异步编程之co——源码分析

为何使用Generator


回顾一下我们之前学习的promise。我们巧妙利用了promise/deferred模式,用链式结构代替了嵌套回调的结构,大大缓解了回调地狱。我们再来看看之前我们举的那个异步串行队列的例子吧!假设我们有一个hello.txt,里面存了一个JSON文件的文件名,我们需要得到JSON文件的message属性的值。步骤如下:

  1. 读取hello.txt文件
  2. 得到JSON文件名,再次读取文件
  3. 得到JSON数据后,进行JSON解析
  4. 获得JSON的message属性

Promise链式调用

这个例子我们之前也是举过非常多次的,我们尝试使用Promise链式结构完成:

//这里的readFile已经是promise化的异步API
readFile('hello.txt', 'utf-8')
.then(function(filename){
return readFile(filename, 'utf-8');
})
.then(JSON.parse)
.then(function(data){
console.log(data.message);
})
.catch(function(err){
console.error(err.message);
});

这样一看下来,promise好像并没有多大问题,思维是线性的,而且错误处理也很友好。我们只需要把上一层执行后的结果通过then()传到下一步执行即可。嗯,但不得不说被链式结构束缚后,我们并没有得到一种酣畅淋漓的编程体验。

同步API

我们要写的爽,当然是要将异步编程得到同步编程的体验,这样我们直接使用同步API看一下是怎样的:

var filename = fs.readFileSync('hello.txt', 'utf-8');
var json = fs.readFileSync(filename, 'utf-8');
console.log(JSON.parse(json).message);

同步的写法清晰明了,而且更符合我们以往的编程习惯。但是同步API阻塞代码这个弊病会在Javascript的单线程执行中非常明显。我们到底有没有一种既可以非常接近同步编程的写法,又可以异步不阻塞代码执行呢?既然问出这种问题,答案当然是有的,就是今天的主角:Generator

Generator使用co写法

Generator,顾名思义是一个构造器,它本身是用来生成迭代器的。它是ES6的新东西,所以你为了使用它,需要在node中开启harmony模式才能体验到它。

$ node --harmony

基于Generator,TJ大神做了一个co库。co在最新的版本里,结合Generator和Promise改善了异步编程的体验,也就是我们之前说的:既可以同步,又不会阻塞

还是一样的例子,我们结合promise的代码和同步API的代码对比看看:

co(function* (){
var filename = yield readFile('hello.txt', 'utf-8');
var json = yield readFile(filename, 'utf-8');
return JSON.parse(json).message;
}).then(console.log, console.error);

非常像有没有,我们不再需要将每一次异步的结果都放在then()中进行处理,我们可以通过类似于同步的写法调用Promise异步API,大大提升编程体验。最后co()返回了一个promise对象,提供我们做最后的数据处理和错误处理。我们从同步API转到co,仅仅需要做到以下几点:

  • co里面传的函数标识符需要加上*号,function*。这也就是Generator函数
  • 调用promise异步API之前,都要加上yield标识符
  • 将需要做最后处理的数据return出来,在then()中进行处理即可

预习Generator


我们在举完异步串行的例子后,这次的文章就接近尾声了。最后我们可以大致了解一下co到底是如何运作的呢?我们会在接下来的文章进行深究,这一次就简单说一说,你当作预习就可以了:

Generator相关

  1. Generator生成迭代器后,等待迭代器的next()指令启动。
  2. 启动迭代器后,代码会运行到yield处停止。并返回一个{value: AnyType, done: Boolean}对象,value是这次执行的结果,done是迭代是否结束。并等待下一次的next()指令。
  3. next()再次启动后。若done属性不为true,则可以继续从上一次停止的地方继续迭代。
  4. 一直重复2,3步骤,直到done为true。

co相关

  1. co内部的迭代器对象是被封装成Promise的。
  2. yield后面跟的必须是一个promise化的异步API,所以next()得到的结果是一个promise对象。
  3. 若迭代没有结束,则co会自动为该异步promise对象的resolve中,增添一个next()。通过前面的异步执行完回调后,再调用next(),使迭代器的代码不断向前执行。
  4. 若迭代结束,则直接调用整个迭代器对象的resolve

总结

或许现在大家看的是一知半解,或许很兴奋想知道更多相关的。若仅仅是想学会用co,我想上面的大概已经足够你看了。但是想更深入,你必须先弄懂promise的原理和Generator的相关特性。最后使用co库一定会得心应手。

接下来,我会先讲一些关于Generator的相关特性,再配合之前说过的promise,深入到co的源码学习中。

异步编程之Generator(1)——领略魅力的更多相关文章

  1. 异步编程之Generator(2)——剖析特性

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  2. javascript异步编程之generator(生成器函数)与asnyc/await语法糖

    Generator 异步方案 相比于传统回调函数的方式处理异步调用,Promise最大的优势就是可以链式调用解决回调嵌套的问题.但是这样写依然会有大量的回调函数,虽然他们之间没有嵌套,但是还是没有达到 ...

  3. (翻译)异步编程之Promise(1):初见魅力

    原文:https://www.promisejs.org/ by Forbes Lindesay 异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2) ...

  4. 异步编程之Promise(3):拓展进阶

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  5. 异步编程之Promise(2):探究原理

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  6. 异步编程之co——源码分析

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  7. net异步编程之await

    net异步编程之await 初探asp.net异步编程之await   终于毕业了,也顺利进入一家期望的旅游互联网公司.27号入职.放肆了一个多月没写代码,好方啊. 另外一下观点均主要针对于await ...

  8. Javascript异步编程之setTimeout与setInterval详解分析(一)

    Javascript异步编程之setTimeout与setInterval 在谈到异步编程时,本人最主要会从以下三个方面来总结异步编程(注意:特别解释:是总结,本人也是菜鸟,所以总结不好的,请各位大牛 ...

  9. 【转】Javascript异步编程之setTimeout与setInterval

    Javascript异步编程之setTimeout与setInterval 转自:http://www.tuicool.com/articles/Ebueua 在谈到异步编程时,本人最主要会从以下三个 ...

随机推荐

  1. oracle数据库导入导出命令!

    Oracle数据导入导出imp/exp 功能:Oracle数据导入导出imp/exp就相当与oracle数据还原与备份. 大多情况都可以用Oracle数据导入导出完成数据的备份和还原(不会造成数据的丢 ...

  2. Navicat数据存放位置和备份数据库路径设置

    navicat的数据库存放位置在什么地方?带着这样的疑问,我们去解决问题,navicat是默认安装,mysql也是默认安装,数据库存在默认用户所在的目录下面. 安装MySQL时,请选择“Custom” ...

  3. UVa 247 Calling Circles【传递闭包】

    题意:给出n个人的m次电话,问最后构成多少个环,找出所有的环 自己想的是:用map来储存人名,每个人名映射成一个数字编号,再用并查集,求出有多少块连通块,输出 可是map不熟,写不出来,而且用并查集输 ...

  4. Couchbase的web管理员后台 查看缓存提示警告 Warning: Editing of document with size more than 2.5kb is not allowed的解决方法

    这个警告仅仅只会发生在web管理员后台,实际在缓存中的数据是不会有影响的(好像默认单个key对应的缓存大小是20M) 但是有时候我们就是想在web后台里面看看到底保存了什么数据,怎么能突破这个限制呢? ...

  5. volley(4) 请求参数:data:[ { bar_remain:XX , bar_code:"XX" , bar_id: XX}], method:GET

    1. 来自于WHCombineBatchFragment.java 2.部分代码 ).).).).port + Url.LABELPRINT + "?data="+strPrint ...

  6. HDU 3555 Bomb (数位DP-记忆化搜索模板)

    题意 求区间[1,n]内含有相邻49的数. 思路 比较简单的按位DP思路.这是第一次学习记忆化搜索式的数位DP,确实比递推形式的更好理解呐,而且也更通用~可以一般化: [数位DP模板总结] int d ...

  7. &引用的问题

    C++中&可以代表引用也代表取地址符. 引用这一方面略微有点头大. 注意: 1)引用的函数可以作为左值 一个简单的作为左值的例子 #include <iostream> using ...

  8. PHP实现站点pv,uv统计(三)

    数据分析脚本如下: //error_reporting(0);date_default_timezone_set("PRC");$environment = get_cfg_var ...

  9. 删除github.com上repository(仓库)的方法

    第一步:打开http://github.com,看到右侧仓库列表.第二步:假设要删除“HiTop”这个参考,点击对应仓库进入详细页面之后,在右侧会看到“Settings”入口. 第三步:进入设置页面之 ...

  10. 树莓派 安装 php

    执行如下命令(注意红色字部分是关键!) sudo apt-get install apache2 php5 libapache2-mod-php5 然后把网页文件复制到 /usr/www 中即可 参考 ...