目录:javascript
前言html
Callbacks前端
Listenershtml5
Promisejava
coder都知道,javascript语言运行环境是单线程的,这意味着任何两行代码都不能同时运行。多任务同时进行时,实质上造成了一个队列,当队列中前一个事件结束时,才执行下一个事件。 若是队列中任何一个事务费时太长,则会形成浏览器假死,阻塞其余事务正常进行,影响用户体验。node
js中将任务执行分为同步模式和异步模式,上面一种即为同步模式,任何比较花时间的代码最好设计成异步模式。经过异步编程方式,便可以达到伪多进程。常见的异步模式主要有callbaks,listeners(一种观察者模式),promise,下面我将会详细描述三者各自的优劣。jquery
这是再常见不过的异步编程方式了,任何前端都写过这样的代码吧:git
function callback(){ console.log('do callbacks'); }; setTimeout(callback,300);
经过这种方式,你能够简单的实现异步编程。这里须要注意,这里的300ms只是理想时间,实际中各浏览器略有差别,主要取决于线程中其余任务所用的时间。这个不具体讨论,其中还涉及requestAnimationFrame等。本身能够google。程序员
此方法缺点是什么想必我不说你也知道,如:es6
function fn1(f){ alert(1); f(); }; function fn2(f){ alert(2); f(); }; function fn3(){ alert(3); }; fn1(function(){ fn2(fn3); });
若是逻辑在复杂一点呢?恐怖看代码的人会一头雾水吧。而promise则能够把这种复杂的嵌套写的一看就明白,下面会讲到。
这也是一种常见的异步编程方式,如:
$elem.on('click',doClick); function doClick(){ //do something };
设计模式中将这种方式称之为命令模式。这种方式的好处是你能够注册多个事件,待事件触发时调用注册的函数。
固然这里不只仅是这种简单的监听方式,我须要将的是一种更抽象的异步编程方式——listeners。经过观察者模式,咱们用一个数组来保存事件名称以及事件绑定的函数方法,当须要触发该事件时,咱们从数组中取出以前的注册的函数。而EventProxy就是这种方式实现的,经过on,all注册,emit触发。如:
var ep = EventProxy.create("template", "data", "l10n", function (template, data, l10n) { _.template(template, data, l10n); }); $.get("template", function (template) { // something ep.emit("template", template); }); $.get("data", function (data) { // something ep.emit("data", data); }); $.get("l10n", function (l10n) { // something ep.emit("l10n", l10n); });
下面我用本身的代码简单的实现这种方式
var Observer = { _callbacks:{}, register:function(name,fn,context){ if(!name){ return; } fn = fn || function(){}; if(!Observer._callbacks[name]){ var list = Observer._callbacks[name] = []; } list.push({fn:fn,context:context}); }, trigger:function(name){ var lists = []; if(!name){ //触发全部 for(var key in Observer._callbacks){ lists.concat(Observer._callbacks[key]); } }else{ //触发指定 lists = Observer._callbacks[name]; } for(var i = 0,len =lists.length;i<len;i++){ var list = lists[i]; var _fn = list.fn; return _fn.apply(list.context || this,[].slice.call(arguments,1)); } } }; Observer.register('fetch',function(param){},this); $.ajax(url,function(res){ Observer.trigger(name,res); });
这种模式在于跨模块异步编程中显得尤其重要。具体的实现逻辑刚才已经讲过,就不具体展开,看代码注释便可。
最后一种方法隆重登场,也是如今讨论最多的异步编程方式。
代码实际上是现实的概括总结并以另一种方式展示,咱们有时候看不懂,是由于写代码方式跟现实有差别。假如代码逻辑这么写:
img1.callThisIfLoadedOrWhenLoaded(function() { // 加载完成 }).orIfFailedCallThis(function() { // 加载失败 });
// 以及…… whenAllTheseHaveLoaded([img1, img2]).callThis(function(){
// 所有加载完成
}).orIfSomeFailedCallThis(function(){
// 一个或多个加载失败
});
我相信非程序员也能看懂吧。这就是咱们要介绍的Promise异步编程方法。现有一些插件库Q.js, when.js ,wind.js ,rsvp.js都是相似实现的,遵照一个通用的、标准化的规范:Promises/A+,jQuery 有个相似的方法叫 Deferred,但不兼容 Promises/A+ 规范,因而会有点小问题,使用需谨慎。jQuery 还有一个 Promise 类型,它实际上是 Deferred 的缩减版,因此也有一样问题。jQuery相关的后续我会分章节逐一讲述,这里不作展开。
Promise异步编程总结起来就是实现了thenable,说的更直白的一点就是我跟你是好朋友,我可能有事须要你帮忙,你随时候着吧,有须要我会第一时间通知你。你跟小王也是好哥们,并一样告诉你须要他帮忙,并第一时间通知他。固然你通知他是在我通知你之后。用伪代码表示:
myself(function(){ callXiaoLi(); }) .then(function(){ //我是小李,你喊我了,我就去喊小王 callXiaowang(); }) .then(function(){ console.log('我是小王'); });
一样,下面咱们会用简单的代码实现以上逻辑。
/** * author by 素人渔夫 * email:dengxiaoming1217@gmail.com * description: 异步编程promise实现 */ (function() { /* * promise */ function EasyPromise() { this._callbacks = []; }; EasyPromise.prototype = { then: function(func, context) { var p; if (this._isdone) { p = func.apply(context, this.result); } else { p = new EasyPromise(); this._callbacks.push(function() { //此时apply context很重要,不然,res中的实例获取不到this var res = func.apply(context, arguments); if (res && typeof res.then === 'function') { res.then(p.resolve, p); }else{ p.resolve(res); }; }); } return p; }, resolve: function() { this.result = arguments; this._isdone = true; for (var i = 0; i < this._callbacks.length; i++) { this._callbacks[i].apply(this.result, arguments); } this._callbacks = []; } }; EasyPromise.when = function() { var AllPromises = [].slice.call(arguments, 0)[0]; var p = new EasyPromise(); var promiseLen = AllPromises.length; var doneParams = {}; AllPromises.forEach(function(item, index) { item.__promise__name = index; doneParams[index] = {}; item.then(checkAllPromiseDone, item); }); var donePromiseCount = 0; function checkAllPromiseDone(data) { donePromiseCount++; doneParams[this.__promise__name] = data; if (donePromiseCount == promiseLen) { doneParams = makeArray(doneParams); p.resolve(doneParams); } }; return p; }; if (typeof define !== 'undefined') { define([], function() { return EasyPromise; }); } else if (typeof exports !== 'undefined') { exports.EasyPromise = EasyPromise; } else { window.EasyPromise = EasyPromise; } })();
then实现比较绕,须要解释一下,如:.then(xx).then()
每次.then时会生成一个新的promise对象, 就是说后一个then是注册给上一个promise对象的,其出发条件依赖于上一个.then时生成的promise。
若是上一个.then 最后返回结果是非promise对象(即没有then方法),则直接 resolve
若是上一个.then最后返回的结果是promise对象,那.then生成的promise必须依赖返回结果中的promise,及内层的 promise.then( ".then时生成的promise".resolve)
可能我这么解释仍是没能解释很清楚,本身去理解吧。
另外,谷歌浏览器已经实现了Promise API。我使用的式chrome 版本 31.0.1650.63。不行你能够在控制台输入如下代码测试:
var p = new Promise(function(resolve,reject){
setTimeout(resolve,100);
setTimeout(reject,300);
});
p.then(function(){
alert('resolve');
},function(){
alert('reject');
});
好了,异步编程的方式就讲这么多,我这里只不过是抛砖引玉,并无详细阐述其api,而是以本身的代码简单实现其原来。这些异步编程方式不只仅限于浏览器环境,服务器环境如nodejs也可以使用。好比EventProxy原本就是在nodejs中发展而来的。
但愿经过个人简单讲述能给你们提供帮忙。若是有错,请帮忙指出,谢谢。
参考:
1.http://www.html5rocks.com/en/tutorials/es6/promises/
2.http://sporto.github.io/blog/2012/12/09/callbacks-listeners-promises/
3.http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html
4.http://javascript.info/tutorial/events-and-timing-depth