Javascript异步编程-耽误对象篇

上篇文章中讲到,运用jquery的ajax要领操纵ajax要求,会遭到回调函数嵌套的题目。固然,jquery团队也发现了这个题目,在2011年,也就是jquery 1.5版本以后,jQuery.Deferred对象为处理这类题目应运而出。以后,zapto等框架也推出雷同api的deferred对象,来举行异步操纵。

在jquery 1.5 版本以后,ajax要求的内部完成被重写。$.ajax要领返回的不再是一个jqXHR对象,而是一个Deferred对象。

运用jquery 1.5版本以后的代码,可以用下面的要领举行一次ajax要求。

// 条件引入jquery
var fetchData = function (url) {
    return $.ajax({
        type: 'get',
        url: url
    });
}

如许一次要求的内容就已完成,$.ajax返回一个$.Deferred对象,那末我们就可以运用$.Deferred对象的api举行一些异步操纵。

出于篇幅,这里只简朴举行引见.done.fail.then$.when这三个要领。

关于每一个$.Deferred对象来讲,实例有多个要领,个中done要领代表异步完成时实行的要领,fail代表异步失利时实行的要领,这两个要领同时依旧返回一个$.Deferred对象的实例。

继承上面的ajax操纵,我们可以如许写胜利和失利的回调:

// fetchData 接上

fetchData()        //实行函数返回一个Deferred对象实例
    .done()        //接收一个函数,ajax要求胜利挪用
    .fail()        //接收一个函数,ajax要求失利挪用
    .done()        //第二个胜利状况的回调要领
    .fail()

一样的关于.then要领,接收两个函数,第一个代表胜利时实行的要领,第二个代表失利时的实行要领。一样的,它也返回一个deferred对象实例。意味着也能举行联缀挪用。

fetchData()
    .then(successFn, errorFn)        //第一次回调
    .then(successFn, errorFn)        //第二次回调

内部完成上,.done 和 .fail 都是基于 .then完成的

fetchData()                            fetchData()
    .done(successFn)    <===>            .then(successFn, null)
    .fail(errorFn)      <===>            .then(null, errorFn)

关于多个ajax同时要求,配合实行同一个回调函数这一点上,jquery有一个$.when要领,接收多个Deferred对象实例,同时实行。

var fetchData = function (url) {
    return $.ajax({
        type: 'get',
        url: url
    });
}

var fetchData1 = fetchData('/path/to/data1');
var fetchData2 = fetchData('/path/to/data2');

$.when(fetchData1, fetchData2, function (data1, data2) {
    // fetchData1 相应为data1
    // fetchData2 相应为data2
})

圆满处理了开辟中的异步题目。

上面的$.ajax只是在$.deferred对象上封装了一层ajax操纵。实际上,真正的$.Deferred对象是如许挪用的:

function printA () {
    var deferred = new $.Deferred();
    setTimeout(function () {
        console.log('A');
        deferred.resolve(' is done.');
    }, 1000);
    return deferred;
}

function printB (msg) {
    var deferred = new $.Deferred();
    setTimeout(function () {
        console.log('B' + msg);
        deferred.resolve();
    }, 1000);
    return deferred;
}

printA()
    .then(printA)
    .then(printB)

每一个函数保护一个Deferred对象,在每一个具有异步操纵的函数实行胜利后,指导全局deferred对象实行下一个函数,到达异步的结果。

新建完成$.Deferred实例deferred以后,挪用deferred.resolve()代表胜利完成相应,deferred.reject()即代表挪用失利相应。

细致诠释源码请拜见另一篇文章,这里我们主要写一下这类挪用体式格局完成的tiny版。

起首我们写一个Callback对象保护我们的回调函数行列

var Callbacks = function () {
    function Callbacks () {
        this.callbacks = [];
    }
    Callbacks.prototype.add = function (fn) {
        this.callbacks.add(fn);
        return this;
    }
    Callbacks.prototype.fire = function () {
        var len = this.callbacks.length;
        if(len) {
            this.callbacks.unshift()();
        }
    }
    return Callbacks;
}

这段代码逻辑很简朴,Callbacks对象有两个要领,分别是add和fire,挪用add则向当前的callbacks数组内新增一个function。fire要领,则从Callbacks中提取最前的一个callback,并实行它。

关于Deferred对象,我们最少须要resolve和reject两个要领。举行胜利和失利的挪用。而且可以举行链式挪用。

var Deferred = function () {
    this.successCbs = new Callbacks();
    this.errorCbs = new Callbacks();
}
Deferred.prototype.then = function (successCb, errorCb) {
    this.successCbs.add(successCb);
    this.errorCbs.add(errorCb);
    return this;
}
Deferred.prototype.resolve = function () {
    this.successCbs.fire();
    return this;
}
Deferred.prototype.reject = function () {
    this.errorCbs.fire();
    return this;
}

如许简朴完成以后,我们新建一个Deferred实例,就可以经由过程链式挪用的体式格局举行异步操纵。

var deferred = new Deferred();
function printA() {
    setTimeout(function () {
        console.log('A');
        deferred.resolve();
    }, 1000);
    return deferred;
}
function printB() {
    setTimeout(function () {
        console.log('B');
        deferred.resolve();
    }, 1000);
}

printA()
    .then(printB)
    .then(printA)

一样的,我们可以封装一个克己tiny-Deferred对象的tiny-ajax要领。

var ajax = function (options) {
    var xhrOptions = {
        type: options.type || 'get',
        url: options.url || '/default/path',
        async: options.async || true
    };
    var deferred = new Deferred();
    var xhr = new XHRHttpRequest();
    xhr.open(xhrOptions.type, xhrOptions.url, xhrOptions.async);
    xhr.onload = function (result) {
        deferred.resolve(result);
    }
    xhr.onerror = function ()
    xhr.send();
    return deferred;
}

详细源代码开源在Github上,迎接pr和issuses。

    原文作者:止水
    原文地址: https://segmentfault.com/a/1190000007763973
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞