有趣-如何解决递归过多导致的栈溢出

sum(0,10000) // 栈溢出

这里有什么方法解决呢?而且必须使用函数递归,不能改用for循环。

我们需要知道事件循环模型,即JS运行时内存图

我们需要知道除了执行栈可以通过存放函数外,还有一个地方可以存放函数,那就时异步任务队列中 ,该队列一般用于存储 异步任务的回调函数,等待执行栈只有全局执行上下文时,就会因为Event Loop而被JS主线程取出放入执行栈中执行。

这样就可以达到将递归函数从执行栈中转移到异步队列中,从而减轻执行栈压力的目的。

function sum(total, i) {

if(i === 0) { // 递归结束条件

return console.log(total)

}

setTimeout(sum,0, total+i, i-1) // 注意这里给sum直接传参会触发sum立即执行,而不是回调执行

}

sum(0,10)

但是出现了一个新问题,结果输出耗时太长。这是为啥呢?

首先,sum(0,10)调用,进入执行栈,函数执行到setTimout,将回调函数sum交给webAPI线程处理,之后sum(0,10)函数调用结束出栈。

需要注意的时 setTimeout设置0s等待,并不是真的0s等待,setTimeout有个最小延迟时间,下面是MDN的描述

也就是说,setTimeout会等待4ms后,再将回调sum加入异步任务队列,而JS主线程通过时间循环从异步任务队列取出回调函数sum也需要时间,所以每次递归函数sum都要经过不少于4ms的时间才可能被加入执行栈执行,

比如 sum(0,10) 就需要不少于 9*4ms = 36ms

sum(0,100) 就需要不少于 360ms, 0.36s

sum(0,1000) 就需要不少于 3600ms, 3.6s

sum(0,10000) 就需要不少于 36000ms,36s

实际情况,肯定比预估时间长

所以我们不能一味的将sum递归函数扔到异步任务队列中。

我们可以让 执行栈先存入部分递归函数调用,当快要到执行栈临界值时,我们就将下次递归函数调用交给异步任务队列,这样就能达到清空执行栈的目的,然后等待4ms左右再将递归函数加入执行栈执行,等到快要到临界值时,再加入异步任务队列以清空执行栈

比如当前执行栈临界值小于 10000 大于 1000,我们就让临界值为1000,

这样sum(0,10000) 只需要不少于 (10000 / 1000) * 4ms  = 40ms 就可以全部完成了。

function sum(total, i) {

if(i === 0) { // 递归结束条件

return console.log(total)

}

if(i%1000 === 0) {

setTimeout(sum,0, total+i, i-1)

} else {

sum(total+i, i-1)

}

}

这样速度就上去了

那有没有可能继续优化?我们再来看下这个JS运行图

当前是将递归函数通过setTimeout加入异步宏任务队列,但是加入异步宏任务队列有几个缺点

1、setTimeout将递归函数加入异步宏任务队列,需要等待至少4ms,有延迟

2、一次事件轮询只能从异步宏任务队列中取出一个递归函数来执行,事件轮询也需要时间

如果将递归函数加入异步微任务队列,就可以解决该缺点

1、加入异步微任务队列没有时间延迟

2、一次事件轮询就可以处理异步微任务队列中所有递归函数

function sum(total, i) {

if(i === 0) { // 递归结束条件

return console.log(total)

}

if (i%1000 === 0) {

Promise.resolve().then(sum.bind(null,total+i,i-1))

} else {

sum(total+i,i-1)

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

给大家分享一些关于HTML的面试题,有需要的朋友可以戳这里获取,先到先得哦。


mgtp.com/2024/03/13/H4lCoPEF.jpg" />

最后

给大家分享一些关于HTML的面试题,有需要的朋友可以戳这里获取,先到先得哦。

[外链图片转存中…(img-GBZYi98n-1712767123898)]
[外链图片转存中…(img-8sYnwqaG-1712767123899)]

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值