资讯专栏INFORMATION COLUMN

你所必须掌握的三种异步编程方法callbacks,listeners,promise

boredream / 719人阅读

摘要:出处你所必须掌握的三种异步编程方法前言都知道,语言运行环境是单线程的,这意味着任何两行代码都不能同时运行。这也是一种常见的异步编程方式,如设计模式中将这种方式称之为命令模式。这就是我们要介绍的异步编程方法。

出处:你所必须掌握的三种异步编程方法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

这种模式在于跨模块异步编程中显得尤为重要。具体的实现逻辑刚才已经讲过,就不具体展开,看代码注释即可。

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91488.html

相关文章

  • ES6-7

    摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...

    mudiyouyou 评论0 收藏0
  • 2017-09-16 前端日报

    摘要:前端日报精选深入理解绑定请使用千位分隔符逗号表示网页中的大数字跨页面通信的各种姿势你所不知道的滤镜技巧与细节代码质量管控复杂度检测中文翻译基于与的三种代码分割范式掘金系列如何构建应用程序冷星的前端杂货铺第期美团旅行前端技术体系 2017-09-16 前端日报 精选 深入理解 js this 绑定请使用千位分隔符(逗号)表示web网页中的大数字跨页面通信的各种姿势你所不知道的 CSS 滤...

    cheng10 评论0 收藏0
  • 浅谈 Promise

    摘要:解决异步编程有两种主要方式事件模型和回调函数。将异步操作以同步操作的流程表达出来,避免了层层嵌套回调函数。方法是的别名,相当于函数的第一个参数传入,第二个参数传入发生错误时的回调函数。 JavaScript 解决异步编程有两种主要方式:事件模型和回调函数。但是随着业务越来越复杂,这两种方式已经不能满足开发者的需求了,Promise 可以解决这方面的问题。为了更好的理解 Promise ...

    李涛 评论0 收藏0
  • [ JS 进阶 ] 异步编程 promise模式 的简单实现

    摘要:为了降低异步编程的复杂性,所以。难理解请参考的误区以及实践异步编程的模式异步编程的种方法 异步编程 javascript异步编程, web2.0时代比较热门的编程方式,我们平时码的时候也或多或少用到,最典型的就是异步ajax,发送异步请求,绑定回调函数,请求响应之后调用指定的回调函数,没有阻塞其他代码的执行。还有像setTimeout方法同样也是异步执行回调的方法。 如果对异步编程...

    svtter 评论0 收藏0
  • JS 异步编程六种方案

    摘要:接下来介绍下异步编程六种方法。六生成器函数是提供的一种异步编程解决方案,语法行为与传统函数完全不同,最大的特点就是可以控制函数的执行。参考文章前端面试之道异步编程的种方法你不知道的中卷函数的含义和用法替代的个理由 前言 我们知道Javascript语言的执行环境是单线程。也就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。 这种模式虽然实现起...

    longmon 评论0 收藏0

发表评论

0条评论

boredream

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<