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%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
最后:
总结来说,面试成功=基础知识+项目经验+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。
面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。
们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。
面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。