您的位置:首页 > 其它

异步函数顺序执行的实现

2017-03-24 20:21 288 查看
这几天在研究实现一个类似Promise思想的库,也不完全一样,主要目标是保证异步事件的执行顺序。

比如有四个异步事件A,B,C,D

需要这样一个执行顺序 A => B => C => D

想了好几种方案。

其中有一种是先建立一个任务链表,执行时修改每个事件的函数内容。

假设现在任务链表顺序已经建立, A => B => C => D。

taskList[function A,function B,function C,function D]


并且四个函数大概是这样:

function A(){
setTimeout(function(){
output.innerText+='第一个异步函数,延迟1秒执行\n';
},1000);
}

function B(){
setTimeout(function(){
output.innerText+='第二个异步函数,延迟2秒执行\n';
},2000);
}

function C(){
setTimeout(function(){
output.innerText+='第三个异步函数,延迟3秒执行\n';
},3000);
}

//D函数是个普通函数
function D(){
output.innerText+='第四个普通函数,无延迟,立即执行\n';
}


还有一个修改函数的函数inject,这个函数执行时可以修改前面四个函数的内容。

function inject(fn){
//对每一个传入的fn进行修改,然后才能执行
}


首先,这个inject函数这样写:

1.先加载fn参数的内容:

function inject(fn){
var source = fn.toString();
}


以函数D为例,得到source的值就是函数D的定义:

inject(D);

//source的值
function D(){
output.innerText+='第四个普通函数,无延迟,立即执行\n';
}


2.给source注入一句执行链表中下一个函数的代码。

这里我想了个办法,先去掉source中
function (){
}
,留下的就是函数体的内容。然后在函数体的最后,加上我想要的代码:

function inject(fn){
var source = fn.toString();
var startPos = source.indexOf('{') + 2,
endPos = source.lastIndexOf('}') -1;
//{ 和 }的位置
//+2是加上{本身和换行的位置
//-1是减去换行的位置

source = source.substring(StartPos,endPos);
//截取出来函数体 alert('第一个函数');

source += "taskList.shift();taskList[0]();";
//写入自定义代码到函数体,链表taskList将首项shift掉,这样把第二个函数移到了第一位,然后再执行。

//使用eval执行修改后的函数体
try{
eval(source);
}
catch(err){
alert(err);
}
}


这样,根据我们注入的代码,执行完inject(D),就会把D函数的内容shift掉,使链表中紧跟D函数的函数成为链表中的第一个函数,从而执行
taskList[0]
,完成链表的顺序执行。

同理,如果A,B,C,D四个函数都是同步的,那么只需要inject这一个函数就可以让他们按顺序执行。

来考虑异步的A,B,C函数,我们依葫芦画瓢,用inject函数的思想,来重写一下A函数中调用的setTimeout方法。

var _setTimeout = setTimeout,
setTimeout = function(fn,time){
var source = fn.toString(),
startPos = source.indexOf('{') + 2,
endPos = source.lastIndexOf('}') -1;
source = source.substring(startPos,endPos) + "taskList.shift();taskList[0]();";

try{
_setTimeout(function(){eval(source)},time);
}
catch(err){
alert(err);
}

}


如此,把调用下一个函数的方法放在定时器内,就完成了对setTimeout的重写。

我们把所有的代码放在一起测试一下

https://html50.github.io/chain.js/test1.html

可以看出,A,B,C,D依次输出了。

在上面的基础上,我们使用构造函数的方法,把代码重新整理一下(结构)。

function Chain(){
this.taskList = [],
this.errorList = [],
this.successList = [],
this.length = arguments.length;
for (i = 0; i < this.length; i++) {
this.taskList.push(arguments[i]);
}

this.next = function() {
if (this.taskList.length > 0){
this.inject(this.taskList[0]);
}
else output.innerText += '任务全部完成';
}

this.start = function() {
this.next();
}

this.setTimeout = function(fn, time) {
//处理setTimeout
}

this.ajax = function() {
//处理ajax
}
}


再写几个异步的函数用于测试

function A() {
setTimeout("output.innerText += '第一个异步函数,延迟1秒执行\\n'", 1000);
}

function B() {
output.innerText += '第二个普通函数,无延迟,立即执行\n';
}

function C() {
setTimeout(function() {
output.innerText += '第三个异步函数,延迟1秒执行\n';
}, 1000);
}

function D(text) {
output.innerText += '第四个普通函数,自定义文字:' + text + ',无延迟,立即执行\n';
}

function E() {
ajax({
url: "test2.html",
beforeSend:function(){
output.innerText += '第五个异步ajax函数,读取本页源码\n';
},
success: function(){
output.innerText += '读取完成,执行下一个函数\n';
}
})
}

function F() {
setTimeout(function() {
output.innerText += '第六个异步函数,延迟1秒执行\n';
}, 1000);
}


测试

var job = new Chain(A, B, C, function a(){
D('函数传参');
}, E, F);
job.start();


完整代码测试页面:

https://html50.github.io/chain.js/test2.html

这样算是基本的功能完成了。根据我的测试,如果要应用到具体的工作中,还是使用PROMISE比较靠谱。而且我写的这个函数对于ajax setTimeout的操作不够灵活,不可以嵌套,不可以混合使用。但是这些都是可以改进的,即进行更深的匹配修改。实现的本身也可以当做一种不同的思路吧。

一个异步读取图片并展示的DEMO:

https://html50.github.io/chain.js/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: