不使用setTimeout和setInterval在页面中实现setInterval和setTimeout效果

前言

昨天面试一家公司,面试官问我,如何在不使用setTimeout和setInterval在页面中实现setInterval和setTimeout效果,我:????。

后来我仔细想了一下,思路就是获取时间戳,然后用递归判断实现。那么思路是这样,下面就代码实现一下吧。

setTimeout的实现

初步实现

function setTimeout_(dalay) {// 第一次的时间戳const timestampFirst = Date.now()// 返回一个promise对象return new Promise(reslove => {// 操作function handle() {// 每一次的时间戳const timestamp = Date.now()// 当时间戳减去后大于延迟时间if ((timestamp - timestampFirst) >= dalay) {// 成功回调reslove()} else {// 递归handle()}}// 初次调用handle()})
}

setTimeout_(10).then(() => {alert(10)
}) 

上面的代码看似没有毛病,但是运行后发现,setTimeout_()里面的值设置小一点没有问题(比如2、3),但是一旦超过,就会造成堆栈溢出,乃至报错。

解决堆栈溢出方法

下面隆重介绍一个人,蹦床函数(trampoline)

蹦床函数(trampoline)就是将 递归执行 转为 循环执行。执行的都是同样的步骤,只是反复执行,就好像在蹦床,跳上去,掉下来,在跳上去…

  • 蹦床函数的实现:
function trampoline(f){while(f && f instanceof Function && falg){ f = f()}return f
} 

它接受一个函数f作为参数。只要f执行后返回一个函数,就继续执行。注意,这里是返回一个函数,然后执行该函数,而不是函数里面调用函数,这样就避免了递归执行,从而就消除了调用栈过大的问题

最终实现

// 定时器
function setTimeout_(dalay) {// 第一次的时间戳const timestampFirst = Date.now()// 返回一个 Promise 对象return new Promise(reslove => {// 具体操作function handle() {// 每一次的时间戳const timestamp = Date.now()// 当时间戳减去后大于延迟时间if ((timestamp - timestampFirst) >= dalay) { // 成功回调reslove()} else { // 不满足条件继续调用return handle}}// 调用蹦床函数、将递归变为循环trampoline(handle)()})
}

// 蹦床函数
function trampoline(f){while(f && f instanceof Function){f = f()}return f
}

setTimeout_(1000).then(res => {alert(1000)
}) 

以上的代码,就能实现效果了

思路:定义一个函数,参数为延迟时间,调用时记录一个第一次时间戳,然后里面返回一个Promise对象,再里面有一个闭包,是执行递归操作的函数,这个函数里面做的事就是记录每一次的时间戳,然后减去第一次的时间戳,得出的就是间隔时间,跟规定的间隔时间作比较,如果大于的话,就调用Promise成功回调。再下面就是将递归转为循环,防止堆栈溢出。最后调用

setInterval的实现

这个跟setTimeout差不多,区别就是这个需要每隔一段时间执行代码,并且需要手动清除

// 如果 falg 为 false就不会继续执行循环操作
let falg = true
// 蹦床函数技术,利用循环
function trampoline(f){while(f && f instanceof Function && falg){f = f()}return f
}

// 计时器
function setInterval_(f, dalay) {// 第一次的时间戳let timestampFirst = Date.now()// 操作function handle() {// 每一次的时间戳const timestamp = Date.now()if ((timestamp - timestampFirst) >= dalay) {// 间隔时间到了就重置第一次时间戳timestampFirst = Date.now()// 调用函数f()}return handle}trampoline(handle)()
}

let count = 0
// 调用
setInterval_(function() {count ++if (count === 3) {falg = false}console.log(count)
}, 1000) 

上面这个代码我定义的是在控制台输入1、2、3,然后关闭

思路:同样是判断时间戳,但是跟setTimeout不一样的是每次执行里面的函数需要重置时间,达到每次执行的效果。并且在蹦床函数里面的while增加一个判断,用来控制计时器的停止。

总结:这种东西了解一下,以后当个吹牛逼资本就可以了,毕竟这性能嘛…

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值