Javascript 为什么以下情况会导致堆栈溢出?
以下功能旨在通过使用Javascript 为什么以下情况会导致堆栈溢出?,javascript,Javascript,以下功能旨在通过使用wait重复创建微任务来阻止宏任务 但为什么它会导致堆栈溢出 我认为await会将递归调用放在一个微任务上,从而清空堆栈(显然我错了) const waitUsingMicroTasks=async(durationMs,start=performance.now())=>{ const wait=async()=>{ 让经过=(performance.now()-start) 如果(经过{ const wait=async()=>{ 让经过=(performance.now
wait
重复创建微任务来阻止宏任务
但为什么它会导致堆栈溢出
我认为await
会将递归调用放在一个微任务上,从而清空堆栈(显然我错了)
const waitUsingMicroTasks=async(durationMs,start=performance.now())=>{
const wait=async()=>{
让经过=(performance.now()-start)
如果(经过
然而,我认为这几乎是等效的,不会导致堆栈溢出:
const waitUsingMicroTasks=async(durationMs,start=performance.now())=>{
const wait=async()=>{
让经过=(performance.now()-start)
如果(经过 waitUsingMicroTasks(1000)
函数中没有执行阻塞任务。因此,它将几乎立即继续执行下一个递归,因为没有递归,所以转义会导致堆栈溢出
在代码中引入一个耗时的任务,如控制台日志,它将运行良好
let count=0;
const waitUsingMicroTasks = async (durationMs, start = performance.now()) => {
const wait = async () => {
let elapsed = (performance.now() - start);
console.log(++count);
(elapsed < durationMs) && (await wait());
}
await wait();
}
waitUsingMicroTasks(1000)
let count=0;
const waitUsingMicroTasks=async(durationMs,start=performance.now())=>{
const wait=async()=>{
让经过=(performance.now()-start);
console.log(++count);
(经过
此递归函数完全在同一个微任务中解析
当遇到wait
时,将创建一个新的微任务,您是正确的。但是await
操作符需要有一个承诺或值,以便隐式地将其包装在新的承诺中并附加回调。这意味着需要先评估正在等待的值,然后才能将其安排为微任务
但是,每次执行wait
都无法在调用下一个wait
之前获得wait
的承诺。因此,实际上堆栈是同步溢出的,没有调度微任务。您永远不会真正获得承诺,因为每个承诺都依赖于要计算的下一个承诺-每个wait()
调用在解决任何wait
s之前的最后一个之后同步发生
您可以通过等待
等待一些不需要计算递归调用的值来强制执行微任务:
const waitUsingMicroTasks = async (durationMs, start = performance.now()) => {
const wait = async () => {
let elapsed = await (performance.now() - start)
if(elapsed < durationMs) await wait()
}
await wait()
}
waitUsingMicroTasks(1000)
const waitUsingMicroTasks=async(durationMs,start=performance.now())=>{
const wait=async()=>{
让经过=等待(performance.now()-start)
如果(经过
在第二个示例中,这个问题不存在,因为您显式创建了一个承诺,并将
wait
作为回调附加到它。通过这种方式,wait
不会立即执行(就像wait
),而是在一段时间后运行Promise.resolve()微任务时调用它。Klaycon的回答几乎涵盖了所有内容,但很容易观察到对wait
的所有调用都是同步发生的(在堆栈上):
const waitUsingMicroTasks=async(durationMs,start=performance.now())=>{
设i=0;
const wait=async()=>{
控制台日志(i);
i+=1;
(i<20)和(wait wait());
}
等待();
console.log('done');
}
waitUsingMicroTasks(5000)
start和performance.now()之间的差异很可能为0,因此函数将立即连续调用自己。确定。(愚蠢)函数的目的是阻止宏任务。但是微任务是异步的,并且确实提供了“协作多任务”(如果你知道我的意思的话)。这在许多系统上都是一个问题,处理器和你正在尝试做的事情几乎肯定会比时钟的分辨率快,试着用计数器来代替,并进行多次迭代。但是。。。我希望每个间接递归调用都有一个全新的堆栈。所以内存会增加,但我不希望堆栈溢出。好的,谢谢。但这一发现仍然让我困惑。因为wait
将微任务排队,所以每次堆栈都应该是新的。我不明白引入延迟如何避免堆栈溢出。@52d6c6af引入延迟只会限制在到达durationMs
之前可以进行的递归调用的数量。这仅仅是因为1000毫秒相对较小。它实际上并没有帮助清除堆栈。而且,这根本不能完成OP试图做的事情,因为它只是在足够的时间过去之前阻塞主线程。在这段时间过去之前,不会安排任务。很好。很好的解释。因此,我如何复制第二个实现,但使用wait
?@52d6c6af问题的关键是每次执行wait
都会同步导致另一次执行wait
。您需要将wait()
的执行推迟到微任务运行之后,这必然意味着将其设置为回调。我认为唯一可以做到这一点的方法是在调用wait()
之前先等待其他东西,如我的示例中所示。我发现这似乎是可行的:if(appeated
…这似乎也可行:if(appeated
@52d6c6af只是澄清一下:第一个await
不会立即计算其操作数,而是在代码中实际遇到它时计算,就像大多数其他运算符一样。签出要刷新的页面await
只是一个一元运算符,当遇到它时,需要在await