ECMAScript 6 的 Promise 是一个非常重要的特性,有了它,JavaScript 异步嵌套的问题算是得到了比较好的解决。同时,Promise 也是 ES7 中 async/await 的基础。
介绍 Promise 基础的文章已经非常多了,在这里就不再讲解 Promise 本身的用法。本文主要介绍利用 Promise 的特性改良异步 Timer 的一种思路。
在产品中,异步加载资源的时候,有时会有一种业务需求,需要执行某个任务,直到任务返回 false 或者超过某个时间限制。
例如,在 360网址导航 中,存在类似这样的代码:
qboot.await(function() {
return hao360.g("channelloading-" + channel);
},
function() {
setTimeout(function() {
if (hao360.g("channelview-" + channel).innerHTML.replace(/^[sxa0u3000]+|[u3000xa0s]+$/g, "") == "") {
hao360.g("channelloading-" + channel).style.display = "block";
}
},
1000);
},
null, 100, 100);
这里的 qboot.await 实际上是一个异步的 timer,由于导航网页上的部分内容是异步加载的,因此用 qboot.await 来确保操作相关 DOM 的时候内容已经加载完毕。
qboot 的具体代码在 github 上可以找到。这里截取其中的片段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* 轮询执行某个task,直到task返回false或者超过轮询最大次数上限
* 如果成功超过轮询上限,执行complete,否则执行abort
* @param task 轮询的任务
* @param step 轮询间隔,以毫秒为单位
* @param max 最大轮询次数
* @param complete 超过最大次数,轮询成功
* @param abort task返回false,轮询被中断
*/
poll : function(task, step, max, complete, abort){
step = step || 100;
if(max == null) max = Infinity;
if(max 0){
complete & complete();
return;
}
if(task() !== false){
setTimeout(function(){
qboot.poll(task, step, max-1, complete, abort);
}, step);
}else{
abort & abort();
}
},
/**
* 等待直到cond条件为true执行success
* 如果等待次数超过max,则执行failer
* @param cond await条件,返回true则执行success,否则继续等待,直到超过等待次数max,执行failer
* @param success await成功
* @param failer await失败
* @param step 时间间隔
* @param max 最大次数
*/
await : function(cond, success, failer, step, max){
qboot.poll(function(){
if(cond()){
success();
return false;
}
return true;
}, step, max, failer);
},
await 方法虽好,但是不够语义化,在这个支持 Promise 的时代,实现这个需求,我们可以有更好的设计思路,产生更加 语义化 的 API:
let promise = wait.every(100).before(10000).until(function(){
return hao360.g("channelloading-" + channel) || false;
});
promise.then(function(channel){