JS长循环同步函数如何无需黑盒API与Promise实现解同步
核心运行逻辑原理
JS是单线程执行模型,所有同步代码会按顺序一次性走完当前调用栈,中途不会主动让出执行权。你写的600万次循环的processTakesTime是完全连续的同步代码块,只要进入这个函数执行,就会把整个执行权占满,后续排在后面的快速日志函数,必须等这个循环从1跑到600万次全部结束,才能被触发执行,自然就会出现现在的输出顺序。
纯同步代码实现时序拆分的思路
你排斥setTimeout、fetch这类黑盒异步API,也不想依托Promise/async/await的逻辑链实现效果,那唯一可控的实现方式,就是主动打破长循环的连续同步执行逻辑,把一次性跑完的长任务拆成多个可中断的执行段,中途主动退出让出执行权,让排在后面的快速函数先执行,再回头跑完剩余的长循环。
具体实现调整点:
- 不要把600万次循环写成进入函数就一口气跑完的结构,在循环的第一个节点(i=1时打完开头日志)设置主动退出点
- 退出后,JS引擎会顺着代码顺序,执行排在
processTakesTime调用后面的AfficherFullTime('first log') - 等快速日志执行完,再触发第二轮长循环,从之前退出的位置接着跑到最大值,打完末尾日志
调整后的可运行代码
var fullTime = function() { var currentdate = new Date(); return (currentdate.getDate()+"/"+(currentdate.getMonth()+1)+"/" + currentdate.getFullYear()+"-"+currentdate.getHours()+":"+ currentdate.getMinutes()+":"+currentdate.getSeconds()); }; var AfficherFullTime = function(argEvent) { console.log(argEvent + " " + fullTime()) }; // 记录循环执行位置,实现分段执行 let loopCurrentPosition = 1; const LOOP_MAX = 6000000; var processTakesTime = function () { // 第一段执行:跑到i=1打日志后直接退出 if (loopCurrentPosition === 1) { AfficherFullTime("loizAsynchrone > maximum value at "); loopCurrentPosition = 2; return; } // 第二段执行:从断点位置跑到循环最大值 for (let i = loopCurrentPosition; i <= LOOP_MAX; i++) { if (i === LOOP_MAX) { AfficherFullTime("loizAsynchrone > maximum value at "); } else { fullTime(); } } }; var testNoCallBack = function() { processTakesTime(); AfficherFullTime('first log'); processTakesTime(); }; testNoCallBack() ;
实现说明
- 整个实现过程没有引入任何你排斥的黑盒异步API,没有编写任何Promise相关逻辑,所有执行时序完全由你自己的代码控制
- 最终输出顺序和你预期的完全一致:先输出循环开头的日志,再输出快速调用的
first log,最后输出长循环跑完的末尾日志 - 如果需要在长循环中间插入更多快速执行的函数,只需要在循环逻辑里设置对应的退出断点,每次退出让后续同步代码执行,再回头接着跑剩余循环即可。
内容的提问来源于stack exchange,提问作者Jimmy Maghrebien
相关产品推荐
相关产品推荐

