• 延时调用--deferred.js原码分析


    有些时候,我们需要等待上一个操作完成之后,才能进行下一步的操作。比如Ajax实现自动提交表单操作的时候,程序需要等待,一旦有返回结果了,则继续进行一下步操作。

    这时deferred.js这个库就产生了,当然,jquery也有这个功能。下面就分析一下这个库的原理:

    复制代码
    /**
     * @fileOverview JSDeferred
     * @author       cho45@lowreal.net
     * @version      0.4.0
     * @license
     * JSDeferred Copyright (c) 2007 cho45 ( www.lowreal.net )
     *
     *  针对deferred的原理进行精简
     */ ; // no warnings for uglify
    
    function Deferred () {
        return this.init();
    }
    //定义静态方法
    Deferred.ok = function(x) {return x} //缺省的成功回调
    Deferred.ng = function(x) {throw x}
    //判断是否为Deferred的实例
    Deferred.isDeferred = function (obj) {
        return !!(obj && obj._id === Deferred.prototype._id);
    };
    
    //这个next是挂在Deferred上的静态方法。与实列方法.next是不同的
    Deferred.next = function(fn){
        var d = new Deferred();
        var img = new Image();
        var handler = function(){
            d.canceller();
            d.calls();
        }
        //这个地方个人认为比较巧秒,它利用了img加载成功或错误回调具有异步的特性。
        //保证完整收集这些.next().next()...
        //事实上官方还用了其它两种方式,确保兼容,如setTimeout....
        img.addEventListener('error',handler,false);
        d.canceller = function(){
            img.removeEventListener('error',handler,false);
        }
        //这里用来触发一个img的加载事件
        img.src = "data:image/png," + Math.random();
        if(fn) d.callback.ok = fn;
        return d;
    }
    
    //这里是用来模拟一个比较耗时的异步过程
    //实践中,可能是取数据的过程,如等待ajax回调
    Deferred.wait = function (n) {
        var d = new Deferred(), t = new Date();
        var id = setTimeout(function () {
            d.calls((new Date()).getTime() - t.getTime());
        }, n * 1000);
        d.canceller = function () { clearTimeout(id) };
        return d;
    };
    
    
    Deferred.prototype = {
        _id : 8888, //随便填写,用来判断是否为Deferred的实例
        init : function(){
            this._next = null;
            //使Deferred.isDeferred 判断为假
            this.callback = {
                ok : Deferred.ok,
                ng : Deferred.ng
            }
            return this;
        },
        next  : function (fun) { return this._post("ok", fun) },
    
        calls  : function (val) { return this._fire("ok", val) },
    
        _post : function (okng, fun) {
            //个人认为,理解这里是关键,
            //._next保存一下实例对象,形成一个链
            this._next =  new Deferred();
            this._next.callback[okng] = fun;
            return this._next;
        },
    
        _fire : function (okng, value) {
            var next = "ok";
    
            value = this.callback[okng](value);
            //这里的value如果不是Deferred的实例
            if (Deferred.isDeferred(value)) {
                //加载下一个任务
                value._next = this._next;
            } else {
                //说明没有下一个任务了
                if (this._next) this._next._fire(next, value);
            }
            return this;
        }
    }
     
    复制代码

     为了配合分析,先给一段测试代码,方便追踪它的流程。

    复制代码
    Deferred.next(function(){
        alert(1)
        return Deferred.wait(3)
    }).next(function(){
        alert(2)
    })
    复制代码

    这里用了一个链式写法,熟jquery的人,对此一定不会陌生。首先是调用Deferred.next()这个方法,返回一个Deferred的实例,然后调用实例上的next()方法。

    意图是要执行第一个函数(弹出1)之后,再执行第二函数(弹出2),仅管在写法是同步的,但是执行的时候却是异步的。 你或许会问, 为什么不直接用setTimeout呢?

    这个问题非常好,其实在很情况下,我觉得确实是可以等价交换。但是setTimeout有一个使用前提,那就是我们必需事先知道上一步运行完成需要等多长时间。而Deferred不需要。

    这时你或许又会问,那为什么不用回调呢?这回我终于没有办法说什么了。直接演示一下这种写法:

    复制代码
    function(next1,next2,next3,...){
        //....
        //....
        next1(function(){
            next2(function(){
                next3(...);
            })
        })
    }
    复制代码

    像竹笋一样,一层套一层。如果函数体长一点,看着不晕吗?再看看deferred的写法:

    复制代码
    Deferred.next(function(){
        next1();
    }).next(function(){
        next2();
    }).next(function(){
        next3()
    }).next(function(){
        //...
    })
    复制代码

    像麻将一样,一字排开,是时髦的链式用法。

    如果你觉得怎么写无所谓,那么要结合起来看了,如果next1是一个耗时不确定的操作,但是要保证执行顺序。怎么破?

    不管你怎么破,反正我是选择Deferred,用定了。

  • 相关阅读:
    lumen 错误&日志
    Composer设置忽略版本匹配的方法
    Laravel框架数据库CURD操作、连贯操作使用方法
    laravel5-目录结构分析
    Lumen 设置 timezone 时区
    phpstorm laravel单元测试 配置
    使用laravel的Eloquent模型获取数据库的指定列
    phpstorm 配置自带webserver ,配置根目录
    使用 OWIN 作为 ASP.NET Web API 的宿主
    angularjs webstorm 单元测试 Package.json
  • 原文地址:https://www.cnblogs.com/sunshq/p/4226774.html
Copyright © 2020-2023  润新知