首页 > 短小强悍的JavaScript异步调用库

短小强悍的JavaScript异步调用库

原文链接:  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);


转载于:https://www.cnblogs.com/lanzhi/p/6467041.html

更多相关:

  •   /*禁止缩放safari浏览器*/ var scale = {disabledSafari: function () {/* 阻止双击放大*/var lastTouchEnd = 0;document.addEventListener("touchstart", function (event) {if (event.touch...

  •   $g.$utils = {/**舒工Ajax-lite 1.0 -- 最精简的ajax自定义访问方法*/ajax: function (o) {var p = o.post, g = o.get, d = p.data, a = p.async, J = 'json', j = p[J], s = g.success, e =...

  •   Sg.js框架核心概念: 1)所有变量、方法、类对象全部都是从属于$g主树,由$g分支出很多$g.变量名、$g.方法、$g.对象id、$g.类;2)获取控件内部属性必须使用公开的get方法获取,禁止直接用访问内部变量方式来获取控件内部变量、属性值;3)修改控件内部属性、绑定方法等都必须使用公开的set方法来操作,禁止直接用访问...

  •  一、ios header导航栏被推起解决方法 1 设置弹出软键盘时自动改变webview的高度 plus.webview.currentWebview().setStyle({ softinputMode: "adjustResize" // 弹出软键盘时自动改变webview的高度 }); 2 增加样式 html...

  • 前端发送Ajax请求到服务器,服务器返回数据这一过程,因原因不同耗时长短也有差别,且这段时间内页面显示空白。如何优化这段时间内的交互体验,以及长时间内服务器仍未返回数据这一问题,是我们开发中不容忽视的重点。 常见的做法是: 1、设置超时时间,一旦时间超过设定值,便终止请求;2、页面内容加载之前,手动增加一个 loading 层。 代码...

  • //获取某一个cookie的值 const getCookie = key => {var k = key, dc = document.cookie;if (dc.length > 0) {var s = dc.indexOf(k + "=");if (s != -1) {s = s + k.length + 1;var e = d...

  • var SGheadMapPoints = {/*obj={ maxLng: minLng: maxLat: minLat: maxCount:最大人数 minCount:最小人数 total:点位数量 }*/get: function (obj) {var arr = [];obj.maxCount || (obj.maxCount...

  • //自动搜索指定的请柬 var alertTipText = "请柬找到了,就在这个网页里面,自己仔细看吧"; var delay = 1 * 1000;//1秒后循环下一页寻找 /*获取子DOM元素在父元素里面的索引位置(是第几个元素)*/ function getNodeListIndex(childNode) {return c...

  •  获取天气情况(不支持跨域) /*json原生获取*/ function getJSON() {var XML;var url = "http://wthrcdn.etouch.cn/weather_mini?city=杭州";if (window.XMLHttpRequest) {XML = new XMLHttpRequest(...

  • 英语的重要性,毋庸置疑!尤其对广大职场人士,掌握英语意味着就多了一项竞争的技能。那,对于我们成人来说,时间是最宝贵的。如何短时间内在英语方面有所突破,这是我们最关心的事情。英语学习,到底有没有捷径可以走,是否可以速成?周老师在这里明确告诉大家,英语学习,没有绝对的捷径走,但是可以少走弯路。十多年的教学经验告诉我们,成功的学习方法可以借...

  • 展开全部 其实IDLE提供了一个显32313133353236313431303231363533e78988e69d8331333365663438示所有行和所有字符的功能。 我们打开IDLE shell或者IDLE编辑器,可以看到左下角有个Ln和Col,事实上,Ln是当前光标所在行,Col是当前光标所在列。 我们如果想得到文件代码...

  • 前言[1]从 Main 方法说起[2]走进 Tomcat 内部[3]总结[4]《Java 2019 超神之路》《Dubbo 实现原理与源码解析 —— 精品合集》《Spring 实现原理与源码解析 —— 精品合集》《MyBatis 实现原理与源码解析 —— 精品合集》《Spring MVC 实现原理与源码解析 —— 精品合集》《Spri...

  • 【本文摘要】【注】本文所述内容为学习Yjango《学习观》相关视频之后的总结,观点归Yjango所有,本文仅作为学习之用。阅读本节,会让你对英语这类运动类知识的学习豁然开朗,你会知道英语学习方面,我们的症结所在。学习英语这类运动类知识,需要把握四个原则第一,不要用主动意识。第二,关注于端对端第三,输入输出符合实际情况第四,通过多个例子...

  • 点云PCL免费知识星球,点云论文速读。文章:RGB-D SLAM with Structural Regularities作者:Yanyan Li , Raza Yunus , Nikolas Brasch , Nassir Navab and Federico Tombari编译:点云PCL代码:https://github.co...