Promise的all和race方法的使用

前文初识Promise中,可以初步了解Promise的简单用法和作用。今天这篇将更进一步,重点介绍promise的两个方法——all和race

先由一个例子引入,仔细观察以下腾讯新闻的页面。

页面上的列表非常多,来看一下network控制面板的情况:

可以看到,这些列表数据不是后端一次请求全部返回给前端的,而是不同的接口,返回不同的列表。页面一加载,就发送了一系列jsonp的请求。

思考一下:页面一下子发送这么多的jsonp请求,如何能得到所有的数据后一起处理呢?

假如页面中发送了四个请求,看以下代码:

$.get("https://cnodejs.org/api/v1/topics?tab=good",function(data){
    console.log(data);

    });
$.get("https://cnodejs.org/api/v1/topics?tab=job",function(data){
    console.log(data);
  
    });
$.get("https://cnodejs.org/api/v1/topics?tab=share",function(data){
    console.log(data);
  
    });
$.get("https://cnodejs.org/api/v1/topics?tab=ask",function(data){
    console.log(data);
    });

不难发现,页面中每个ajax的回调都是一个独立的作用域。这样看来,将四个data组合在一起貌似不太可能,有人可能会定义一个全局数组变量和一个定时器,写出如下代码:

var arr = []
$.get("https://cnodejs.org/api/v1/topics?tab=good", function (data) {
    arr.push(data)
    console.log(data);
});
$.get("https://cnodejs.org/api/v1/topics?tab=job", function (data) {
    arr.push(data)
    console.log(data);

});
$.get("https://cnodejs.org/api/v1/topics?tab=share", function (data) {
    arr.push(data)
    console.log(data);

});
$.get("https://cnodejs.org/api/v1/topics?tab=ask", function (data) {
    arr.push(data)
    console.log(data);
});
setTimeout(function () {
    console.log(arr);
}, 1500)

运行后,这段代码有时候可能会成功,有时候会失败,究其原因就是请求的回调函数返回的时间不确定。

这种方法不行,那再试着用回调嵌套回调来解决看看:

$.get("https://cnodejs.org/api/v1/topics?tab=ask", function (data) {
    arr.push(data);
    $.get("https://cnodejs.org/api/v1/topics?tab=job", function (data) {
        arr.push(data);
        $.get("https://cnodejs.org/api/v1/topics?tab=good", function (data) {
            arr.push(data);
            $.get("https://cnodejs.org/api/v1/topics?tab=share", function (data) {
                arr.push(data);
                console.log(arr)
            })
        })

    })
})

这种方法同时拿到了所有回调的数据,并进行处理。但是,咱们看看network的控制面板吧:

看下总时间和waterfall,花的时间是四次ajax的时间的总和!!这简直是对宝贵时间的巨大浪费有木有!!

那么,有没有四个ajax同时发送请求又可以在同一个作用域操作数据的方式呢?

当然有了,看代码:

var count = 0;
var arr = [];
function handle() {
    if (count === 4) {
        console.log(arr);
    }
}
$.get("https://cnodejs.org/api/v1/topics?tab=good", function (data) {
    arr.push(data);
    count++;
    handle()
})
$.get("https://cnodejs.org/api/v1/topics?tab=job", function (data) {
    arr.push(data);
    count++;
    handle()
})
$.get("https://cnodejs.org/api/v1/topics?tab=share", function (data) {
    arr.push(data);
    count++;
    handle()
});
$.get("https://cnodejs.org/api/v1/topics?tab=ask", function (data) {
    arr.push(data);
    count++;
    handle()
});

此时的思路是这样的:定义一个全局数组,一个计数器变量,一个检查器函数。每次回调执行,计数器都会加1,并把数据塞进数组,并且会执行检查器函数,当检查器满足条件时,证明所有数据返回,并且数据都保存到了一个数组里,可以对其进行操作了。

这时再来看network,寻找一下成就感。

可以明显感觉到:时间变短了,而且是并发发送。相比上面的回调嵌套,节省了很多时间。

貌似问题是解决了,但再回头审视一下这段代码,会发现这种处理方式,增加了额外的计数器变量count,额外的全局数组,额外的检查器函数。代码量的激增,无形中增加了后续维护的压力。如何能更轻松地实现同样的效果呢?

这时就需要出动Promise的all方法了。

// 封装一个promise;
var p = function (url) {
    return new Promise(function (resolve, reject) {

        $.get(url, function (data) {
            resolve(data);

        })
    })
}
Promise.all([
    p("https://cnodejs.org/api/v1/topics?tab=good"),
    p("https://cnodejs.org/api/v1/topics?tab=share"),
    p("https://cnodejs.org/api/v1/topics?tab=ask"),
    p("https://cnodejs.org/api/v1/topics?tab=job")
]).then(function (results) {
    console.log(results);
})

还是通过看network:

可以发现:同样是并发请求,Promise的all方法的参数是一个数组,数组每一项其实就是一个promise对象,每个promise对象内部都会resolve一团数据,这团数据会被之后的then方法接收,then方法接收到的数据也是一个数组,正好对应all方法里面每个promise对象resolve出来的数据。

没有全局数组,没有计数器变量,没有检查器函数。是不是很赞?

接着来介绍同样很酷炫的race方法

和前面一样,先从讨论一个需求入手:在页面上发送了一个ajax请求,如果1000ms内没有返回就进行默认的操作。

用最传统的方式如何实现以上需求?如下:

var ajax = $.get("https://cnodejs.org/api/v1/topics?tab=good",function(data){
  console.log(data);
  console.log("数据返回,清除定时器");
  clearTimeout(timer)
});

var timer = setTimeout(function(){
  console.log("超时了");
  ajax.abort();
},3200)

注意,这里出现了一个新方法:abort——在ajax发送后,回调未执行之前取消ajax的回调的方法。仔细观察代码,这两段代码高度耦合,ajax回调里面清除定时器,定时器里面取消ajax。

用promise的race方法就可以避免这种耦合,看代码:

var p1 = function(url) {
    return new Promise(function(resolve, reject) {
        $.get(url,
        function(data) {
            resolve(data);
        })
    })
}
var p2 = function() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve("超时了,进行默认操作");
        },
        1200)
    })
}
Promise.race([p1("https://cnodejs.org/api/v1/topics?tab=good"), p2()]).then(function(result) {
    console.log(result);
})

通过修改时间来测试一下代码,then方法中接受的数据,根据时间的不同有时可能是data,有时可能是‘超时了...’,并且两段代码不会相互耦合。

race方法的的参数也是一个数组,数组每一项都是promise对象。

和all方法不同的是,all会把所有promise对象resolve的数据传递到then中,race只传递最先返回的那个promise resolve的值。race的中文意思是竞赛:谁最先返回就将谁的值传递下去。

熟练使用promise的all和race会使你的代码易于维护、简洁明了,快打开编辑器测试一下上面的代码吧!

有疑问可给此公众号发送信息。

欢迎转发!

本文分享自微信公众号 - nodejs全栈开发(geekclass)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-11-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券