Promise 与定时器
介绍 Promise 基础的文章已经非常多了,在这里就不再讲解 Promise 本身的用法。本文主要介绍利用 Promise 的特性改良异步 Timer 的一种思路。
在产品中,异步加载资源的时候,有时会有一种业务需求,需要执行某个任务,直到任务返回 false 或者超过某个时间限制。
例如,存在类似这样的代码:
qboot.await(function() {
return hao360.g("channelloading-" + channel);
},
function() {
setTimeout(function() {
if (hao360.g("channelview-" + channel).innerHTML.replace(/^[\s\xa0\u3000]+|[\u3000\xa0\s]+$/g, "") == "") {
hao360.g("channelloading-" + channel).style.display = "block";
}
},
1000);
},
null, 100, 100);
这里的 qboot.await 实际上是一个异步的 timer,由于导航网页上的部分内容是异步加载的,因此用 qboot.await 来确保操作相关 DOM 的时候内容已经加载完毕。
qboot 的具体代码在 github 上可以找到。这里截取其中的片段:
/**
* 轮询执行某个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){
//do sth.
});
wait-promise 库
我们可以基于 Promise 实现一套优雅的 API,把它封装成 wait-promise 库。
根据常见应用场景设计如下 API:
check
wait.check(condition)
check 总是返回一个 promise,如果 condition 抛出未处理的异常或者返回一个 false 值,那么 promise 的状态为 reject,否则 resolve 该 promise。
例如:
let i = 1;
let promise = wait.check(function(){
return i < 1;
});
promise.catch(function(err){
console.log(err.message); //will be check failed
});
until
wait.until(condition)
until 每隔若干时间间隔(默认为100ms),检查一次 condition,直到当前 condition 不抛异常也不返回 false 值为止,满足条件后将 promise 的状态置为 resolve。
例如:
let i = 0;
let promise = wait.until(function(){
return ++i >= 10;
});
promise.then(function(){
console.log(i); //i will be 10
});
ES7 的用法:
let until = wait.until;
async function foo(){
let i = 0;
await until(() => ++i > 10);
bar();
}
till
wait.till(condition)
与 until 类似,不同之处在于 condition 返回 true 的时候 till 将 promise 的状态置为 resolve。同样与 until 相反,如果 till 的 condition 抛异常, promise 的状态为 reject 。
例如(通常情形下和 until 用法一样):
let i = 0;
let promise = wait.till(function(){
return ++i >= 10;
});
promise.then(function(){
console.log(i); //i will be 10
});
before
wait.before(millisec).until(condition)
before 通常和 until 组合使用,给 until 轮询限制一个最长时间,超过时间之后,如果 condition 还是抛异常或者返回 false,则将 promise 的状态置成 reject。
例如:
let i = 0;
let promise = wait.before(200).until(function(){
return ++i >= 10;
});
promise.catch(function(err){
console.log(i, err.message); //2 check failed
});
通常我们可以用来判断资源是否加载成功(包含超时):
var promise = wait.before(5000).until(function(){
return typeof Babel !== "undefined";
});
promise.then(function(){
code = Babel.transform(code, { presets: ["es2015-loose", "react", "stage-0"] }).code;
}).catch(function(){
console.error("Cannot load babeljs.");
});
after
wait.after(millisec).check(condition)
after 通常和 check 组合使用,超过一定时间之后再执行 condition 检查。
例如:
let i = 1;
setTimeout(function(){
++i;
}, 0);
let promise = wait.after(1000).check(function(){
return i > 200;
});
after 也可以和 util 组合使用,因为 wait.util 第一次总是会尽快执行(然后才是每经过固定时间间隔执行一次),所以使用 after 可以让 until 第一次执行也延迟。
介绍 Promise 基础的文章已经非常多了,在这里就不再讲解 Promise 本身的用法。本文主要介绍利用 Promise 的特性改良异步 Timer 的一种思路。
在产品中,异步加载资源的时候,有时会有一种业务需求,需要执行某个任务,直到任务返回 false 或者超过某个时间限制。
例如,在 360网址导航 中,存在类似这样的代码:
qboot.await(function() {
return hao360.g("channelloading-" + channel);
},
function() {
setTimeout(function() {
if (hao360.g("channelview-" + channel).innerHTML.replace(/^[\s\xa0\u3000]+|[\u3000\xa0\s]+$/g, "") == "") {
hao360.g("channelloading-" + channel).style.display = "block";
}
},
1000);
},
null, 100, 100);
这里的 qboot.await 实际上是一个异步的 timer,由于导航网页上的部分内容是异步加载的,因此用 qboot.await 来确保操作相关 DOM 的时候内容已经加载完毕。
qboot 的具体代码在 github 上可以找到。这里截取其中的片段:
/**
* 轮询执行某个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){
//do sth.
});
wait-promise 库
我们可以基于 Promise 实现一套优雅的 API,把它封装成 wait-promise 库。
根据常见应用场景设计如下 API:
check
wait.check(condition)
check 总是返回一个 promise,如果 condition 抛出未处理的异常或者返回一个 false 值,那么 promise 的状态为 reject,否则 resolve 该 promise。
例如:
let i = 1;
let promise = wait.check(function(){
return i < 1;
});
promise.catch(function(err){
console.log(err.message); //will be check failed
});
until
wait.until(condition)
until 每隔若干时间间隔(默认为100ms),检查一次 condition,直到当前 condition 不抛异常也不返回 false 值为止,满足条件后将 promise 的状态置为 resolve。
例如:
let i = 0;
let promise = wait.until(function(){
return ++i >= 10;
});
promise.then(function(){
console.log(i); //i will be 10
});
ES7 的用法:
let until = wait.until;
async function foo(){
let i = 0;
await until(() => ++i > 10);
bar();
}
till
wait.till(condition)
与 until 类似,不同之处在于 condition 返回 true 的时候 till 将 promise 的状态置为 resolve。同样与 until 相反,如果 till 的 condition 抛异常, promise 的状态为 reject 。
例如(通常情形下和 until 用法一样):
let i = 0;
let promise = wait.till(function(){
return ++i >= 10;
});
promise.then(function(){
console.log(i); //i will be 10
});
before
wait.before(millisec).until(condition)
before 通常和 until 组合使用,给 until 轮询限制一个最长时间,超过时间之后,如果 condition 还是抛异常或者返回 false,则将 promise 的状态置成 reject。
例如:
let i = 0;
let promise = wait.before(200).until(function(){
return ++i >= 10;
});
promise.catch(function(err){
console.log(i, err.message); //2 check failed
});
通常我们可以用来判断资源是否加载成功(包含超时):
var promise = wait.before(5000).until(function(){
return typeof Babel !== "undefined";
});
promise.then(function(){
code = Babel.transform(code, { presets: ["es2015-loose", "react", "stage-0"] }).code;
}).catch(function(){
console.error("Cannot load babeljs.");
});
after
wait.after(millisec).check(condition)
after 通常和 check 组合使用,超过一定时间之后再执行 condition 检查。
例如:
let i = 1;
setTimeout(function(){
++i;
}, 0);
let promise = wait.after(1000).check(function(){
return i > 200;
});
after 也可以和 util 组合使用,因为 wait.util 第一次总是会尽快执行(然后才是每经过固定时间间隔执行一次),所以使用 after 可以让 until 第一次执行也延迟。