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前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
最后
今天的文章可谓是积蓄了我这几年来的应聘和面试经历总结出来的经验,干货满满呀!如果你能够一直坚持看到这儿,那么首先我还是十分佩服你的毅力的。不过光是看完而不去付出行动,或者直接进入你的收藏夹里吃灰,那么我写这篇文章就没多大意义了。所以看完之后,还是多多行动起来吧!
可以非常负责地说,如果你能够坚持把我上面列举的内容都一个不拉地看完并且全部消化为自己的知识的话,那么你就至少已经达到了中级开发工程师以上的水平,进入大厂技术这块是基本没有什么问题的了。
谓是积蓄了我这几年来的应聘和面试经历总结出来的经验,干货满满呀!如果你能够一直坚持看到这儿,那么首先我还是十分佩服你的毅力的。不过光是看完而不去付出行动,或者直接进入你的收藏夹里吃灰,那么我写这篇文章就没多大意义了。所以看完之后,还是多多行动起来吧!
可以非常负责地说,如果你能够坚持把我上面列举的内容都一个不拉地看完并且全部消化为自己的知识的话,那么你就至少已经达到了中级开发工程师以上的水平,进入大厂技术这块是基本没有什么问题的了。