你所必须掌握的三种异步编程方法callbacks,listeners,promise
目录:
前言
Callbacks
Listeners
Promise
前言
coder都知道,javascript语言运行环境是单线程的,这意味着任何两行代码都不能同时运行。多任务同时进行时,实质上形成了一个队列,当队列中前一个事件结束时,才执行下一个事件。 如果队列中任何一个事务费时太长,则会造成浏览器假死,阻塞其他事务正常进行,影响用户体验。
js中将任务执行分为同步模式和异步模式,上面一种即为同步模式,任何比较花时间的代码最好设计成异步模式。通过异步编程方式,即可以达到伪多进程。常见的异步模式主要有callbaks,listeners(一种观察者模式),promise,下面我将会详细描述三者各自的优劣。
Callbacks
这是再常见不过的异步编程方式了,任何前端都写过这样的代码吧:
function callback(){
console.log('do callbacks');
};
setTimeout(callback,300);
通过这种方式,你可以简单的实现异步编程。这里需要注意,这里的300ms只是理想时间,实际中各浏览器略有差异,主要取决于线程中其他任务所用的时间。这个不具体讨论,其中还涉及requestAnimationFrame等。自己可以google。
此方法缺点是什么想必我不说你也知道,如:
function fn1(f){
alert(1);
f();
};
function fn2(f){
alert(2);
f();
};
function fn3(){
alert(3); }; fn1(function(){ fn2(fn3); });
如果逻辑在复杂一点呢?恐怖看代码的人会一头雾水吧。而promise则可以把这种复杂的嵌套写的一看就明白,下面会讲到。
Listeners
这也是一种常见的异步编程方式,如:
$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); });
这种模式在于跨模块异步编程中显得尤为重要。具体的实现逻辑刚才已经讲过,就不具体展开,看代码注释即可。
Promise
最后一种方法隆重登场,也是现在讨论最多的异步编程方式。
代码其实是现实的归纳总结并以另外一种方式展现,我们有时候看不懂,是因为写代码方式跟现实有差异。假如代码逻辑这么写:
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
你所必须掌握的三种异步编程方法callbacks,listeners,promise的更多相关文章
- js的三种异步处理
js的三种异步处理 Promise 对象 含义: Promise是异步编程的一种解决方案, 优点: 相比传统回调函数和事件更加合理和优雅,Promise是链式编程(后面会详细讲述),有效的解决了令 ...
- 同源策略(same-origin policy)及三种跨域方法
同源策略(same-origin policy)及三种跨域方法 1.同源策略 含义: 同源是指文档的来源相同,主要包括三个方面 协议 主机 载入文档的URL端口 所以同源策略就是指脚本只能读取和所属文 ...
- .Net MVC 导入导出Excel总结(三种导出Excel方法,一种导入Excel方法) 通过MVC控制器导出导入Excel文件(可用于java SSH架构)
.Net MVC 导入导出Excel总结(三种导出Excel方法,一种导入Excel方法) [原文地址] 通过MVC控制器导出导入Excel文件(可用于java SSH架构) public cl ...
- JSON三种数据解析方法(转)
原 JSON三种数据解析方法 2018年01月15日 13:05:01 zhoujiang2012 阅读数:7896 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blo ...
- .NET三种异步模式(APM、EAP、TAP)
APM模式: .net 1.0时期就提出的一种异步模式,并且基于IAsyncResult接口实现BeginXXX和EndXXX类似的方法. .net中有很多类实现了该模式(比如HttpWebReque ...
- javascript的异步编程方法
一,callback 回调函数 即函数f1和函数f2的关系是f1(f2()); f2作为f1()的回调函数,在f1执行过程中就开始执行f2,先执行线程的主要逻辑,将比较耗时的任务放在后面执行. 回调函 ...
- EF Core中避免贫血模型的三种行之有效的方法(翻译)
Paul Hiles: 3 ways to avoid an anemic domain model in EF Core 1.引言 在使用ORM中(比如Entity Framework)贫血领域模型 ...
- JavaScript的三种工业化调试方法
JavaScript的三种工业化玩法 软件工程中任何的语言如果想要写出健壮的代码都需要锋利的工具,当然JavaScript也不例外,很多朋友刚入门的时候往往因为工具选的不对而事半功倍,JavaScri ...
- Javascript异步编程方法总结
现在我们有三个函数,f1, f2, f3 按正常的思路我们会这样写代码: function f1 (){}; function f2 (){}; function f3 (){}; //在这里调用函数 ...
随机推荐
- webserver 发布问题
1:web.config <system.web> <compilation debug="true" targetFramework="4. ...
- ASP.NET状态保持:ViewState
ViewState是ASP.NET的.aspx页面特有的,是页面级的状态保持.一般用在内网系统和网站后台. namespace WebFormTest.TestCollect { public par ...
- POI中设置Excel单元格格式样式(居中,字体,边框等)
创建sheet什么的就不多说了,直接进入正题 HSSFCellStyle cellStyle = wb.createCellStyle(); 一.设置背景色: cellStyle.setFillF ...
- 2013-10-25笔记,css: mini-width, 标准居中,样式中*号使用,背景图像位置定位
mini-width:设置元素的最小宽度.該屬性值會對元素的寬度設置一個最小限制.因此,元素可以比制定值寬,但不能比制定值窄.不允許指定負值. 完美的居中佈局: body{text-align: ce ...
- C++ string 转 char*
string 转到 char* char name[20]; string sname=GatherName[n]; strcpy(name,sname.c_str());
- jquery 点击页面其他地方实现隐藏菜单功能
1.给页面文档添加一个点击事件函数,在函数内实现隐藏菜单功能. $('html').click(function(){//Hide the menus if visible});//用$(docume ...
- STM32F0xx_FLASH编程(片内)配置详细过程
Ⅰ.概述 关于数据的储存,我觉得编程的人基本上都会使用到,只是看你储存在哪里.STM32的芯片内部FLASH都是可以进行编程的,也就是说可以拿来储存数据.但是,很多做一些小应用程序开发的人都没有利用好 ...
- uC/OS-II之系统函数20160526
任务管理 1 OSTaskCreate() 建立一个新任务.任务的建立可以在多任务环境启动之前,也可以在正在运行的任务中建立.中断处理程序中不能 建立任务.一个任务可以为无限循环的结构. 函数原型:I ...
- Python脚本控制的WebDriver 常用操作 <十> 层级定位
下面将使用WebDriver来模拟操作一个层级定位元素的操作 测试用例场景 在实际的项目测试中,经常会有这样的需求:页面上有很多个属性基本相同的元素,现在需要具体定位到其中的一个.由于属性基本相当,所 ...
- 《高性能javascript》读书笔记
1.每个<script>加载时都会阻塞其他文件(图片.音乐等)的同时加载,同时浏览器会在js代码执行时停止渲染Dom.所以为了减少界面加载的卡顿和空白发生,应尽力将js代码或者文件放在&l ...