前文初识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会使你的代码易于维护、简洁明了,快打开编辑器测试一下上面的代码吧!
有疑问可给此公众号发送信息。
欢迎转发!