一道面试题:sleep函数实现

源码地址:https://github.com/flitrue/ts-sleep

先看题目:

  1. 请实现如下函数;
function sleep(timeout: number): Promise {
  // 待实现
}
  1. 请修改上述函数的接口和实现,让该函数支持取消。也就是说,可以在sleep没有结束前,promise提前resolve;
  2. 请为2中实现的函数提供几组单元测试,示意即可;
  3. 如何证明2中实现的函数不会内存泄露。

下面我们开始分析这道面试题:

  1. 明白sleep函数是干嘛用的;
  2. 从伪代码中我们可以看出,这道题用到了typescript;
  3. 而且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;

今天是2020年6月1日儿童节,祝各位大朋友、小朋友,今天只做小孩,保持我们的童心,敢做梦、敢实现。

关注我的公众号(全栈码农)或加我wx(w34638660),和我一起成长。

https://juejin.im/post/5ed4e95ff265da771b2fe3cb

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论