先看题目:
- 请实现如下函数;
function sleep(timeout: number): Promise {
// 待实现
}
- 请修改上述函数的接口和实现,让该函数支持取消。也就是说,可以在sleep没有结束前,promise提前resolve;
- 请为2中实现的函数提供几组单元测试,示意即可;
- 如何证明2中实现的函数不会内存泄露。
下面我们开始分析这道面试题:
- 明白sleep函数是干嘛用的;
- 从伪代码中我们可以看出,这道题用到了typescript;
- 而且sleep函数返回一个promise类型的值,说明需要用promise实现。
sleep函数是可以暂停函数在一段时间内执行,在没有promise的时代,sleep函数基本都是通过setTimeout控制的,promise出现以后,貌似前端coder发现了新大陆,各种花里胡哨的撸它,创造出各种各样的异步编程库,比如目前最流行的axios。
首先我们不考虑ts实现,下面是我第一版的实现:
function sleep(timeout) {
return new Promise(resolve => setTimeout(resolve, timeout))
}
看着很简单又简洁,通过返回一个Promise实例,到了指定的时间执行resolve方法。但是这种实现只满足了面试题中的第一个问题。接下来我们需要考虑如何中断promise,实现提前结束让程序继续执行。
也许喜欢专研的同学,看到这里肯定会想到axios库中有个CancelToken,可以取消请求。CancelToken基本使用如下:
var CancelToken = axios.CancelToken;
var source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
下面是我的改进,在promise增加一个cancel属性,返回resolve方法
function sleep(timeout) {
var res;
var promise = new Promise(function (resolve) {
res = resolve;
setTimeout(function () {
resolve('done');
}, timeout);
});
promise.cancel = res;
return promise;
}
第一个和第二个问题解决了,我们写几个测试用例,验证我们的函数正确性。
// 测试用例1
function test1() {
console.info('test1 start');
sleep(2000).then(function (res) {
console.log('2s later ' + res);
});
}
test1(); // result: 2s later done
// 测试用例2
function test2() {
console.info('test2 start');
var p = sleep(5000);
p.then(function (res) {
console.log(res);
});
setTimeout(function () {
p.cancel('2s later end');
}, 2000);
}
test2(); // result: 2s later end
你以为这样就结束了吗?第四个问题我们还没有解决,既然问了这么问题,那必然是有原因的。
当sleep暂停时间还没有结束前,我们中断了sleep函数,setTimeout函数其实还占用着内存,导致内存泄露,所以我们需要将定时器在执行cancel的时候取消掉。
function sleep(timeout) {
var res,timer;
var promise = new Promise(function (resolve) {
res = resolve;
timer = setTimeout(function () {
resolve('done');
}, timeout);
});
var cancel = function (data) {
res(data);
clearTimeout(timer)
}
promise.cancel = cancel;
return promise;
}
到此,支持中断的sleep函数书写完毕,你看废了吗? 接下来我将ts版的实现贴出来,仅供大家参考,如何有更好的实现,可以在留言区告诉我。
type CancelablePromise = Promise<any> & { cancel: any }
function sleep(timeout: number): CancelablePromise {
let res: (v: string) => void, timer: NodeJS.Timer;
let promise = new Promise(resolve => {
res = resolve
timer = setTimeout(() => {
resolve('done')
}, timeout)
}) as CancelablePromise
let cancel = function (data: string) {
res(data)
clearTimeout(timer)
}
promise.cancel = cancel;
return promise
}
export default sleep;
关注我的公众号(全栈码农)或加我wx(w34638660),和我一起成长。