资讯专栏INFORMATION COLUMN

Javascript异步编程-延迟对象篇

callmewhy / 659人阅读

摘要:上篇文章中讲到,使用的方法操作请求,会受到回调函数嵌套的问题。第一次回调第二次回调内部实现上,和都是基于实现的对于多个同时请求,共同执行同一个回调函数这一点上,有一个方法,接受多个对象实例,同时执行。

上篇文章中讲到,使用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://www.ucloud.cn/yun/91314.html

相关文章

  • Javascript 异步编程-基础

    摘要:在前端这个领域里面,请求非常常见,相信每一个前端都写过下面的代码前提引入上面这段代码中的和被称为回调函数。多个请求希望有一个共同的回调响应继续使用最初的方法,假设有多个请求,希望在全部完成后执行回调函数。异步编程延迟对象篇 在前端这个领域里面,ajax请求非常常见,相信每一个前端er都写过下面的代码: // 前提引入jquery $.ajax({ type: get, ...

    huaixiaoz 评论0 收藏0
  • JavaScript异步编程原理

    摘要:一异步编程原理显然,上面这种方式和银行取号等待有些类似,只不过银行取号我们并不知道上一个人需要多久才会完成。下面来探讨下中的异步编程原理。 众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着。在事件队列中加一个延时,这样的问题便可以得到缓解...

    lidashuang 评论0 收藏0
  • javascript异步编程详解

    摘要:在服务器端,异步模式甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有请求,服务器性能会急剧下降,很快就会失去响应。第三是,捕捉不到他的错误异步编程方法回调函数这是异步编程最基本的方法。 前言 你可能知道,Javascript语言的执行环境是单线程(single thread)。所谓单线程,就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面...

    huangjinnan 评论0 收藏0
  • 前端_JavaScript

    摘要:为此决定自研一个富文本编辑器。例如当要转化的对象有环存在时子节点属性赋值了父节点的引用,为了关于函数式编程的思考作者李英杰,美团金融前端团队成员。只有正确使用作用域,才能使用优秀的设计模式,帮助你规避副作用。 JavaScript 专题之惰性函数 JavaScript 专题系列第十五篇,讲解惰性函数 需求 我们现在需要写一个 foo 函数,这个函数返回首次调用时的 Date 对象,注意...

    Benedict Evans 评论0 收藏0
  • 搞懂 JavsScript 异步 —  事件轮询

    showImg(https://segmentfault.com/img/bVbjYU7?w=2000&h=1333); 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! JavsScript 是一门单线程的编程语言,这就意味着一个时间里只能处理一件事,也就是说 JavaScript 引擎一次只能在一个线程里处理一条语句。 虽然单线程简化了编程代码,因为你不必太担心并发引出的问...

    adam1q84 评论0 收藏0

发表评论

0条评论

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