异步编程之Generator(1)——领略魅力

系统 1747 0

异步编程系列教程:

  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)——领略魅力


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论