原文连接:
7 lines JavaScript library for calling asynchronous functions
翻译人员:
铁锚
翻译时间: 2014年02月18日
示例地址:
简短强悍的JavaScript异步调用库在线调试
对于博文
20行完成一个JavaScript模板引擎 的备受好评我感到很惊讶,并决定用此文章介绍使用我常用的另外一个小巧实用的工具.咱们知道,在浏览器中的 JavaScript 绝大部分的操做都是异步的(asynchronous),因此咱们一直都须要使用回调方法,而有时难免陷入回调的泥淖而欲死欲仙.
假设咱们有两个 functions ,咱们顺序地在一个后面执行完后调用另外一个。他们都操做同一个变量。第一个设置它的值,第二个使用它的值。
var value;
var A = function() {
setTimeout(function() {
value = 10;
}, 200);
}
var B = function() {
console.log(value);
}
那么,如今若是咱们运行
A();B(); 咱们将在控制台看到输出为
undefined . 之因此会这样是由于 A 函数使用了异步方式设置 value 。咱们能作的就是传一个回调函数给A,并让函数A在执行完后执行回调函数。
var value;
var A = function(callback) {
setTimeout(function() {
value = 10;
callback();
}, 200);
};
var B = function() {
console.log(value);
};
A(function() {
B();
});
这样确实有用,但想象一下加入咱们须要运行5个或更多方法时将会发生什么。一直传递回调函数将会致使混乱和很是不雅观的代码。
好的解决办法是写一个工具函数,接受咱们的程序并控制整个过程。让咱们先从最简单的开始:
var queue = function(funcs) {
// 接下来请看,董卿???
}
接着,咱们要作的是经过传递A和B来运行该函数 -
queue([A, B])。咱们须要取得第一个函数并执行它。
var queue = function(funcs) {
var f = funcs.shift();
f();
}
若是执行这段代码,您将看到一个
TypeError: undefined is not a function。这是由于 A函数没收到回调参数但却试图运行它。让咱们换一种调用方法。
var queue = function(funcs) {
var next = function() {
// ...
};
var f = funcs.shift();
f(next);
};
在 A执行完后会调用 next 方法。将下一步操做放在
next 函数列表中是个很好的作法。咱们能够将代码归拢在一块儿,并且咱们可以传递整个数组(即使数组中有不少函数等待执行)。
var queue = function(funcs) {
var next = function() {
var f = funcs.shift();
f(next);
};
next();
};
到了这一步,咱们基本上达到了咱们的目标。即函数A 执行后,接着会调用 B,打印出变量的正确值。这里的关键是 shift 方法的使用。它删除数组的第一个元素并返回该元素。一步一步执行下去, funcs数组就会变成 empty(空的)。因此,这可能会致使另外一个错误。为了证实这一观点,让咱们假设咱们仍然须要运行这两个功能,但咱们不知道他们的顺序。在这种状况下,两个函数都应该接受回调参数(callback )并执行它。
var A = function(callback) {
setTimeout(function() {
value = 10;
callback();
}, 200);
};
var B = function(callback) {
console.log(value);
callback();
};
固然,咱们会获得
TypeError: undefined is not a function.
要阻止这一点,咱们应该检查funcs数组是否为空。
var queue = function(funcs) {
(function next() {
if(funcs.length > 0) {
var f = funcs.shift();
f(next);
}
})();
};
咱们所作的就是定义 next 函数并调用它。这种写法减小了一点代码。
让咱们试着想象尽量多的状况。好比当前执行功能的 scope 。函数内的 this 关键字可能指向了全球的 window 对象。,若是咱们能够设置本身的scope 固然是件很酷的事情。
var queue = function(funcs, scope) {
(function next() {
if(funcs.length > 0) {
var f = funcs.shift();
f.apply(scope, [next]);
}
})();
};
咱们为这个tiny 类库增长了一个参数。接着咱们使用 apply 函数,而不是直接调用 f(next),来设置scope 并将参数 next 传递进去。一样的功能,但漂亮多了。
咱们须要的最后一个特性,就是是函数间传递参数的能力。固然咱们不知道具体会有多少参数将被使用。这就是为何咱们须要使用 arguments 变量的缘由。你可能知道,该变量在每一个 JavaScript函数中都是可用的,表明了传进来的参数。它就和一个数组差很少,但不彻底是。由于在 apply 方法中咱们须要使用真正的数组,使用一个小窍门来进行转换。
var queue = function(funcs, scope) {
(function next() {
if(funcs.length > 0) {
var f = funcs.shift();
f.apply(scope, [next].concat(Array.prototype.slice.call(arguments, 0)));
}
})();
};
下面是测试的代码:
// 测试代码
var obj = {
value: null
};
queue([
function(callback) {
var self = this;
setTimeout(function() {
self.value = 10;
callback(20);
}, 200);
},
function(callback, add) {
console.log(this.value + add);
callback();
},
function() {
console.log(obj.value);
}
], obj);
执行后的输出为:
30
10
为了代码的可读性和美观,咱们将部分相关的代码移到一行内:
var queue = function(funcs, scope) {
(function next() {
if(funcs.length > 0) {
funcs.shift().apply(scope || {}, [next].concat(Array.prototype.slice.call(arguments, 0)));
}
})();
};
你能够
点击这里查看并调试相关代码 ,完整的测试代码以下:
var queue = function(funcs, scope) {
(function next() {
if(funcs.length > 0) {
funcs.shift().apply(scope || {}, [next].concat(Array.prototype.slice.call(arguments, 0)));
}
})();
};
var obj = {
value: null
};
queue([
function(callback) {
var self = this;
setTimeout(function() {
self.value = 10;
callback(20);
}, 200);
},
function(callback, add) {
console.log(this.value + add);
callback();
},
function() {
console.log(obj.value);
}
], obj);