关于generator异步编程的理解以及如何动手写一个co模块

generator出现之前,想要实现对异步队列中任务的流程控制,大概有这么一下几种方式:

  • 回调函数
  • 事件监听
  • 发布/订阅
  • promise对象

第一种方式想必大家是最常见的,其代码组织方式如下:

function fn(url, callback){
 var httpRequest;    //创建XHR
 httpRequest = window.XMLHttpRequest ? new XMLHttpRequest() :  
    window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : undefined;

 httpRequest.onreadystatechange = function(){
  if(httpRequest.readystate === 4 && httpRequest.status === 200){  //状态判断
   callback.call(httpRequest.responseXML);
  }
 };
 httpRequest.open("GET", url);
 httpRequest.send();
}

fn("text.xml", function(){    //调用函数
 console.log(this);   //此语句后输出
});

console.log("this will run before the above callback.");  //此语句先输出 

对于一个普通的ajax异步请求来说,我么在请求开始的时候就要告诉他请求成功之后所要执行的动作,因此就可以类似以这种方式组织代码,控制异步流程。这种调用方式最大的问题就是回调黑洞的问题,一层回调也还好,但涉及到二层、三层、n层的时候就让代码变得复杂很难维护。

第二种方式自己在前段时间使用backbone.js作为技术栈的项目的开发中深有体会,对于每一个ajax请求都对其分配一个自定义事件,在ajax成功返回数据的时候,就会触发自定义的事件完成接下来的动作,控制异步流程,代码如下:

第三种方式和第二种的方式性质上有些类似,如果从发布订阅的角度来看,on方法相当于订阅者/观察者,trigger方法相当于发布者。原理上来说无非就是维护一个“消息中心”的数组,通过on方法订阅的事件都会推入“消息中心”数组,最后发布的时候将会匹配“消息中心”数组的事件,进而执行相应的流程。

我们通过jquery的sub/pub插件完成一个很简单的演示。

首先,f2向"信号中心"jQuery订阅"done"信号。

 jQuery.subscribe("done", f2);

function f1(){

    setTimeout(function () {

      // f1的任务代码

      jQuery.publish("done");

    }, 1000);

  }

f1();

jQuery.publish("done")的意思是,f1执行完成后,向"信号中心"jQuery发布"done"信号,从而引发f2的执行。

第四种方式promise范式,先看一段代码:

我们只要并且仅需要new一个promise对象,就会发现promise对象的参数函数已经执行了,隔两秒之后输出"执行完成"。

接下来再看一段其实际应用的场景代码:

从本质上来看,Promise是一个构造函数,其本身有all、reject、resolve等方法,同时其原型上有then、catch等方法。通过其用Promise new出来的对象自然就有then、catch方法。然后可以通过then方法中的回调函数,获取到上一段异步操作中返回(通过resolve)的数据。从而实现对异步操作的流程控制。

但我的每个函数都得被promise对象包装一下,同时一大堆的then...真是一个听蛋疼的事儿...

综上所述对于异步流程的控制,都有其自身的缺陷,我们最理想的方式便是像操作同步流程那样实现对异步流程的控制,试想一下这样的异步操作流程(加了层层包装,proxy便是发送一个异步请求,接下来的代码便是获取到异步操作返回的数据,细节可暂时忽略):

这感觉就是真他妈的舒服,怎么实现这么一个让人很爽的东西呢,于是我们的主角---伟大的Generator函数登场了。

先理解这么自己悟的一句话:

"javascript是单线程的,顺序执行一段代码,执行到了异步操作,按正常的逻辑走的话就是主队列中的代码继续执行,这时异步队列中的代码还未执行,我们继续执行的代码也就会发生报错。那么解决问题的关键就是,我们能够手动控制代码的向下执行,配合一个东西监听到异步操作的已经正常返回了之后,去手动的操作代码的执行流程,这样的话就实现了已同步的方式控制异步代码的执行"

那么问题变成了解决两个问题。

1、我们是如何实现对于异步操作是否成功返回的监听。

2、如何手动操作代码的向下执行。

对于第一个问题,我们采用的方案是使用promise对象的方式,Promise 的编程思想便是,用于“当xx数据准备完毕,then执行xx动作”这样的场景,用在这里再适合不过。

对于第二个问题,我们便是采用伟大的generator生成器函数,其中的yield特性,可以使我们手动的控制代码的向下执行。

接下来我们实际的解决一个问题:实现对于读取文件异步操作的控制,当读取完文件之后打印读取的内容。

我们依赖于node环境,首先通过promise对其进行封装,实现数据成功的监听。我们手下代码如下:

var fs = require(‘fs‘);
var readFile = function(fileName) {
    return new Promise(function(resolve,reject) {
        fs.readFile(fileName, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        })
    })
} 

有了这个东西,我们便可以通过其then()表达式,"当数据加载完后,执行某个动作"。那我们执行的动作是啥,自然就是执行下一步的代码的操作。继续看代码:

var gen = function* () {
    var f1 = yield readFile(‘/Users/dongzhiqiang/Desktop/demo.txt‘);
    var f2 = yield readFile(‘/Users/dongzhiqiang/Desktop/demo.txt‘);

    console.log(‘<<<<<<<<<<<<<<<<<<<<<<<‘,f1.toString());
    console.log(‘>>>>>>>>>>>>>>>>>>>>>>>>‘,f2.toString());
}

这个就是一个generator函数的表达式,在这个函数里面,遇到generator就会执行类似于return的操作。我们通过next()便可以实现手动的控制代码的向下执行。

那么我们如何控制代码的执行流程呢,看下面一段:

var g = gen();

g.next().value.then(function(data){
  g.next(data).value.then(function(data){
    g.next(data);
  });
});

这段的具体解释就是,我们通过promise封装的对象实现了对于异步操作数据返回的监听,当数据返回的时候,我们就通过next()执行下一步的操作,同时把上步操作的值带入到下一个阶段的执行流程之中。

但是上面这段操作很是蛋疼啊,我们要的是一个能通用的操作流程函数。那么我们继续对这段循环操作进行封装:

function run(gen){
  var g = gen();

  function next(data){
    var result = g.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }

  next();
}

run(gen);

于是一个非常简单的co模块便诞生了。
最终代码如下:

我们把函数放到run的执行器里面,便实现了同步操作异步代码的过程。

未完待续...

时间: 2024-08-02 01:17:52

关于generator异步编程的理解以及如何动手写一个co模块的相关文章

异步编程系列第03章 自己写异步代码

p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提高下英文,用我拙劣的英文翻译一些重要的部分,纯属娱乐,简单分享,保持学习,谨记谦虚. 如果你觉得这件事儿没意义翻译的又差,尽情的踩吧.如果你觉得值得鼓励,感谢留下你的赞,愿爱技术的园友们在今后每一次应该猛烈突破的时候,不选择知难而退.在每一次应该独立思考的时候,不选择随波逐流,应该全力以赴的时候,不选择尽力而

一个例子读懂 JS 异步编程: Callback / Promise / Generator / Async

JS异步编程实践理解 回顾JS异步编程方法的发展,主要有以下几种方式: Callback Promise Generator Async 需求 显示购物车商品列表的页面,用户可以勾选想要删除商品(单选或多选),点击确认删除按钮后,将已勾选的商品清除购物车,页面显示剩余商品. 为了便于本文内容阐述,假设后端没有提供一个批量删除商品的接口,所以对用户选择的商品列表,需要逐个调用删除接口. 用一个定时器代表一次接口请求.那思路就是遍历存放用户已选择商品的id数组,逐个发起删除请求del,待全部删除完成

JavaScript异步编程(一) 深入理解JavaScript事件

JavaScript异步编程 深入理解JavaScript事件 ?事件的调度 JavaScript事件处理器在线程空闲之前不会运行 线程的阻塞 var start = new Date(); // setTimeout和setInterval的计时精度比期望值差 setTimeout(function(){ var end = new Date(); console.log('Time elapsed', end - start, 'ms'); }, 500); while(new Date -

让我们再为C#异步编程Async正名

本文版权归博客园和作者吴双本人共同所有.转载和爬虫必须在显要位置注明出处:http://www.cnblogs.com/tdws 半年前翻译了一系列很糟糕的异步编程文章,用异步的常用语来说:"在将来的某个时间" 我还会重新翻译Async in C#5.0 http://www.cnblogs.com/tdws/p/5617242.html 写在前面 异步编程在处理并发方面被使用的越来越多,之所以说上面一句话,是为了区分多线程编程.各位司机都知道,实际上异步编程的核心目标正并发处理.可还

.net异步编程の-------异步编程模型(APM)

术语解释: APM               异步编程模型, Asynchronous Programming Model EAP                基于事件的异步编程模式, Event-based Asynchronous Pattern TAP                基于任务的异步编程模式, Task-based Asynchronous Pattern 一.异步编程 APM即异步编程模型的简写(Asynchronous Programming Model),大家在写代

异步编程(二)基于事件的异步编程模式 (EAP)

一.引言 在上一个专题中为大家介绍了.NET 1.0中提出来的异步编程模式--APM,虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题--不支持对异步操作的取消和没有提供对进度报告的功能,对于有界面的应用程序来说,进度报告和取消操作的支持也是必不可少的,既然存在这样的问题,微软当然也应该提供给我们解决问题的方案了,所以微软在.NET 2.0的时候就为我们提供了一个新的异步编程模型,也就是我这个专题中介绍的基于事件的异步编程模型--EAP. 实现了基于事件的异步模式的类将

C#中的异步编程:await和async

根据代码示例来学习, 创建一个函数来模拟时间消耗的方法,此处为GetSomeThing函数. 要使用异步编程,需要使用一个async修饰的方法来包装调用GetSomeThing函数,此函数的返回值为Task类型,该类型表示进行并行运算的任务引用.此处示例为ConsumeManyTime函数. 现在就可以直接使用异步方式了,参考TestOne()函数中的代码,其实就是直接调用第二步中的函数ConsumeManyTime(); 总结:异步编程虽然示例三步,但是实际上就是两步的事情,第一步是模拟应用,

.NET 中的 async/await 异步编程

前言 最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入的,由于之前对于异步编程不是很了解,所以花费了一些时间学习一下相关的知识,并整理成这篇博客,如果在阅读的过程中发现不对的地方,欢迎大家指正. 同步编程与异步编程 通常情况下,我们写的C#代码就是同步的,运行在同一个线程中,从程序的第一行代码到最后一句代码顺序执行.而异步编程的核心是使用多线程,通过让不同的线程执行不同的任务,实现不同代码的并行运行. 前台线程与后台线程 关于多线程,早在.NET2

angularjs系列之轻松使用$q进行异步编程

 第一部分关于js中的异步编程 异步编程简单的说就是你写了一段代码,但他不会按照你书写代码的顺序立即执行,而是等到程序中发生了某个事件(如用户点击了某个按钮,某个ajax请求得到了响应)才去执行这段代码,而且这段代码可能执行一次(如一个ajax请求得到了响应).也可能执行很多次或者不执行(一个按钮被点击了许多次或者0次)这就是所谓的异步编程. 有两种异步程序模式单次执行模式和监听执行模式.像ajax请求这样的就是属于单次执行模式,请求.回调只会进行一次.像事件绑定就属于监听执行模式,只要事件发生