关于generator异步编程的理解以及如何动手写一个co模块
generator出现之前,想要实现对异步队列中任务的流程控制,大概有这么一下几种方式:
回调函数
事件监听
发布/订阅
promise对象
第一种方式想必大家是最常见的,其代码组织方式如下:
function fn(url, callback){ var() :
windowActiveXObject new ActiveXObject("MicrosoftXML;
);
}
};
);
();
}
fn("textxml", function(){//调用函数
consolelog(this); //此语句后输出});
consolelog("this will run before the above callback");//此语句先输出
对于一个普通的ajax异步请求来说,我么在请求开始的时候就要告诉他请求成功之后所要执行的动作,因此就可以类似以这种方式组织代码,控制异步流程。这种调用方式最大的问题就是回调黑洞的问题,一层回调也还好,但涉及到二层、三层、n层的时候就让代码变得复杂很难维护。
第二种方式自己在前段时间使用backbonejs作为技术栈的项目的开发中深有体会,对于每一个ajax请求都对其分配一个自定义事件,在ajax成功返回数据的时候,就会触发自定义的事件完成接下来的动作,控制异步流程,代码如下:
第三种方式和第二种的方式性质上有些类似,如果从发布订阅的角度来看,on方法相当于订阅者/观察者,trigger方法相当于发布者。原理上来说无非就是维护一个“消息中心”的数组,通过on方法订阅的事件都会推入“消息中心”数组,最后发布的时候将会匹配“消息中心”数组的事件,进而执行相应的流程。
我们通过jquery的sub/pub插件完成一个很简单的演示。
首先,f2向"信号中心"jQuery订阅"done"信号。
jQuerysubscribe("done", f2);function f1(){
setTimeout(function () {
// f1的任务代码
jQuerypublish("done");
}, 1000);
}
f1();
jQuerypublish("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) {fsreadFile(fileName, function(err, data) { if (err) return reject(err);
resolve(data);
})
})
}
有了这个东西,我们便可以通过其then()表达式,"当数据加载完后,执行某个动作"。那我们执行的动作是啥,自然就是执行下一步的代码的 *** 作。继续看代码:
var gen = function () { var f1 = yield readFile('/Users/dongzhiqiang/Desktop/demotxt'); var f2 = yield readFile('/Users/dongzhiqiang/Desktop/demotxt');consolelog('<<<<<<<<<<<<<<<<<<<<<<<',f1toString());
consolelog('>>>>>>>>>>>>>>>>>>>>>>>>',f2toString());
}
这个就是一个generator函数的表达式,在这个函数里面,遇到generator就会执行类似于return的 *** 作。我们通过next()便可以实现手动的控制代码的向下执行。
那么我们如何控制代码的执行流程呢,看下面一段:
var g = gen();gnext()valuethen(function(data){
gnext(data)valuethen(function(data){
gnext(data);
});
});
这段的具体解释就是,我们通过promise封装的对象实现了对于异步 *** 作数据返回的监听,当数据返回的时候,我们就通过next()执行下一步的 *** 作,同时把上步 *** 作的值带入到下一个阶段的执行流程之中。
但是上面这段 *** 作很是蛋疼啊,我们要的是一个能通用的 *** 作流程函数。那么我们继续对这段循环 *** 作进行封装:
function run(gen){ var g = gen(); function next(data){ var result = gnext(data); if (resultdone) return resultvalue;resultvaluethen(function(data){
next(data);
});
}
next();
}
run(gen);
于是一个非常简单的co模块便诞生了。
最终代码如下:
我们把函数放到run的执行器里面,便实现了同步 *** 作异步代码的过程
同步和异步的区别:
1、同步就是说多个任务之间是有先后关系的,一个任务需要等待另一个任务执行完毕才能继续执行。
2、异步就是说多个任务之间没有先后关系,不需要相互等待各做各的事。
同步编程方法:
1、信号量
2、互斥量
异步无需考虑资源冲突,不需特别处理。
协程函数:async def 函数名。35+
协程对象:执行协程函数()得到的协程对象。
35之后的写法:
37之后的写法:更简便
await后面 跟 可等待的对象。(协程对象,Future,Task对象 约等于IO等待)
await实例2:串行执行。 一个协程函数里面可以支持多个await ,虽然会串行,但是如果有其他协程函数,任务列表也在执行,依然会切换。只是案例中的main对应执行的others1和others2串行 。 await会等待对象的值得到之后才继续往下走。
以上就是关于关于generator异步编程的理解以及如何动手写全部的内容,包括:关于generator异步编程的理解以及如何动手写、linux下C编程多线程同步和异步的区别,如何能实现程序的同步、Python异步编程4:协程函数,协程对象,await关键字等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)