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的执行器里面,便实现了同步操作异步代码的过程。

未完待续...

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

  1. Java网络编程中异步编程的理解

    目录 前言 一.异步,同步,阻塞和非阻塞的理解 二.异步编程从用户层面和框架层面不同角度的理解 用户角度的理解 框架角度的理解 三.为什么使用异步 四.理解这些能在实际中的应用 六.困惑 参考文章 前 ...

  2. C#异步编程のawait和async关键字来写异步程序

    一.await和async关键字 .Net平台不断推出了新的异步编程模型,在.net4.5中加入了关键字await和async,顾名思义,await是指方法执行可等待,即可挂起直到有结果(不是必须立即 ...

  3. 关于javascript异步编程的理解

    在开发手机app的时候,要使用ajax想向后台发送数据.然后遇到了一个bug,通过这个bug,理解了ajax异步请求的工作原理.下面是登录页面的源代码. <!DOCTYPE html> & ...

  4. ES6 Generator 异步编程解决方案&&&promise

    Generator: 是比promise更高级的解决方案 next   yield function 后加* 状态机 generator语法糖 长轮询  接口常查询 ================= ...

  5. C# async/await异步编程深入理解

    异步函数简介 一般指 async 修饰符声明得.可包含await表达式得方法或匿名函数. 声明方式 异步方法的声明语法与其他方法完全一样, 只是需要包含 async 关键字.async可以出现在返回值 ...

  6. 深入理解spring中的AOP原理 —— 实现MethodInterceptor接口,自已动手写一个AOP

      1.前言 AOP是面向切面编程,即“Aspect Oriented Programming”的缩写.面对切面,就是面向我们的关注面,不能让非关注面影响到我们的关注面.而现实中非关切面又必不可少,例 ...

  7. linux系统编程:自己动手写一个who命令

    who命令的作用用于显示当前有哪些用户登录到系统. 这个命令执行的原理是读取了系统上utmp文件中记录的所有登录信息,直接显示出来的 utmp文件在哪里呢? man who的时候,在手册下面有这么一段 ...

  8. linux系统编程:自己动手写一个pwd命令

    pwd命令:打印当前的工作目录 我们都知道每个目录下面都有两个特殊的目录( . 和 .. ), .: 当前目录, ..: 上层目录,  每个目录都有一个i节点与之相关联 ghostwu@ubuntu: ...

  9. linux系统编程:自己动手写一个ls命令

    ls用于列举目录内容,要实现这个功能,毫无疑问,需要读取目录,涉及到两个api: opendir:DIR *opendir(const char *name), 传文件名,返回一个指针,指向目录序列 ...

随机推荐

  1. 原生js和jquery实现图片轮播特效(转)

    本文给大家分享的是使用原生JS和JQ两种方法分别实现相同的图片轮播特效,十分的实用,也非常方便大家对比学习原生js和jQuery,有需要的小伙伴可以参考下. 1)首先是页面的结构部分对于我这种左右切换 ...

  2. Alamofire源码解读系列(四)之参数编码(ParameterEncoding)

    本篇讲解参数编码的内容 前言 我们在开发中发的每一个请求都是通过URLRequest来进行封装的,可以通过一个URL生成URLRequest.那么如果我有一个参数字典,这个参数字典又是如何从客户端传递 ...

  3. JS日期加减指定天数

    JS中没有直接操作日期加减的方法,只能通过Date对象获取当前天数加减之后setDate,以此来达到操作日期的目的 JS中对指定日期加减指定天数,具体方法如下: function addDate(da ...

  4. Spark_总结五

    Spark_总结五 1.Storm 和 SparkStreaming区别 Storm                      纯实时的流式处理,来一条数据就立即进行处理 SparkStreaming ...

  5. Struts2学习笔记④

    刚才看书发现了一个问题,就是ActionSupport和Action接口的区别没搞清楚,弄得我以为我之前的代码写错了.其实ActionSupport已经实现了Action接口了,实际开发中也很少使用A ...

  6. 1011: [HNOI2008]遥远的行星

    1011: [HNOI2008]遥远的行星 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 2241  Solved ...

  7. android 中判断WiFi是否可用的可靠方法 ,android 是否联网

    http://alex-yang-xiansoftware-com.iteye.com/blog/619841 在一些程序中,需要从网上下载数据,或者通过其他方式对网络产生流量,当wifi不可用时应该 ...

  8. Nginx uWSGI web.py 站点搭建

    一.安装nginx 在安装nginx前,需要先装nginx的依赖包. 1.如果没有yum则先安装yum   删除原有的yum  rpm -aq|grep yum|xargs rpm -e --node ...

  9. 存储结构与邻接矩阵,深度优先和广度优先遍历及Java实现

    如果看完本篇博客任有不明白的地方,可以去看一下<大话数据结构>的7.4以及7.5,讲得比较易懂,不过是用C实现 下面内容来自segmentfault 存储结构 要存储一个图,我们知道图既有 ...

  10. mac 下安装securecrt

    下载文件链接中附带的文件. 1.先找到secureCRT的包内容,进入MACOS文件夹.替换crack中的secureCRT文件. 2.断网.进入软件,显示你的验证码过期.点continue.选择手动 ...