为什么从无限while循环内部调用setTimeout时不被调用

时间:2018-12-10 03:25:40

标签: javascript settimeout infinite-loop

我遇到一个有趣的问题,我试图从infite循环中调用一个函数(其主体中具有setTimeout),但从未被调用,但是当循环更改为非infite时,调用是发生了!

这有效:-

var bar = function() {
    setTimeout(function() {
        console.log("1");
    }, 0);
};

var count = 4;
while(count > 0){
  bar();
  count --;
}

这永远都行不通。 :-

var bar = function() {
  setTimeout(function() {
    console.log("1");
  }, 1000);
};

while (true) {
  bar();
}

任何人都可以解释这里发生了什么! 如何通过无限循环调用函数体内具有setTimeout的函数?

5 个答案:

答案 0 :(得分:7)

这是因为事件循环的工作方式。诸如超时之类的异步事件会排队等待,并在脚本runs-to-completion的主体之后 进行处理。这意味着它正在等待您的$xl = New-Object -ComObject excel.application $xldirection = [Microsoft.Office.Interop.Excel.XLDirection] $wb = $xl.workbooks.open($filename) $wsData = $wb.worksheets.item(1) $wsdata.activate() $start = $wsData.Range("E25") $data = $wsData.Range($start, $start.End($xldirection::xldown)) $wschart = $wb.sheets.add(); $chart = $wschart.shapes.addchart().chart $chart.setSourceData($data) 循环完成,然后才开始查看while回调。这是为什么您不想使用长时间运行的同步代码(例如javascript中的巨型循环)来阻塞线程的原因之一。在无限的setTimeout循环旋转时,没有其他事情发生。

答案 1 :(得分:1)

首先让我们看看这个(无限)代码如何执行

CURRENT EXECUTION                               WAITING FOR EXECUTION (IN QUEUE)
===================                             ================================

=> variable bar is assigned to a function
=> while (true)
=> call bar()
=> inside bar()
=> setTimeout function will be sent to queue    => Execute setTimeout’s function after 1000ms(1)
=> while (true)                         
=> call bar()                           
=> inside bar()                         
=> setTimeout function will be sent to queue    => Execute setTimeout’s function after 1000ms(2)
=> call bar()                           
=> inside bar()                         
=> setTimeout function will be sent to queue    => Execute setTimeout’s function after 1000ms(3)
.
.
. while (true) => happens forever           => waits forever…

如果您要在无限循环内调用带有setTimeout的函数,则可以使用类似的方法

var bar = function() {
    setTimeout(function() {
        console.log("1");
        runBar();
   }, 1000);
};

function runBar() {
    bar();
}
runBar();

答案 2 :(得分:1)

这是因为您的JavaScript代码是单线程的。无限的while循环使浏览器很忙,没有时间执行其他异步功能,包括bar函数。

答案 3 :(得分:1)

这是事件在javascript中循环的方式。 setTimeout()回调被添加到宏任务队列中。仅当没有其他JavaScript在执行中或调用堆栈为空时才处理宏任务队列。因此,setTimeout()回调函数无法执行,直到while循环(及其包含的代码)完成并将控制权返回给浏览器为止。

在示例1中:

var bar = function() {
    setTimeout(function() {
        console.log("1");
    }, 0);
};

var count = 4;
while(count > 0){
  bar();
  count --;
}

while执行完成并将控制权返回给浏览器(调用堆栈为空),因此所有setTimeout()回调都被一个接一个地执行。

在example2中:

var bar = function() {
  setTimeout(function() {
    console.log("1");
  }, 1000);
};

while (true) {
  bar();
}

这是一个无限循环,因此控制永远不会返回到浏览器或调用堆栈永远不会为空,因此事件循环无法处理宏任务队列,因此永远不会执行setTimeout()回调。

答案 4 :(得分:0)

  

如何从无限调用函数体内具有setTimeout的函数   循环吗?

喜欢:

let done = false;
let count = 0;

const bar = () => {
  setTimeout(() => {
    console.log(`bar ${count}`);
    count++;
  }, 5); // delay#1
}

const eventLoopQueue = () => {
  return new Promise(resolve => 
    setImmediate(() => {
      console.log('event loop');
      setTimeout(() => {
        if (count > 10) {
          done = true;
        }
        resolve();
      }, 5) //delay#2 should be equal or greater than delay#1
    })
  );
}

const run = async () => {
  while (!done) {
    bar();
    await eventLoopQueue();
  }
}

run().then(() => console.log('Done'));

输出:

event loop
bar 0
event loop
bar 1
event loop
bar 2
event loop
bar 3
event loop
bar 4
event loop
bar 5
event loop
bar 6
event loop
bar 7
event loop
bar 8
event loop
bar 9
event loop
bar 10
Done

当您需要在有条件退出的长时间循环或无限循环中执行某项操作并且可以访问事件(鼠标单击,任何socket.io事件等)时,此方法非常有用。

let done = false;

const eventEmitter = setInterval(() => {
    console.log('Hello! Are you here? Hello!');
  }, 0)

// This setTimeout just ends infinite loop in some time
// For demonstation purpose only
setTimeout(() => {
  done = true;
  clearInterval(eventEmitter);
}, 20);

// This gives us access to event loop queue
// If the events wait in the queue they will be released
const eventLoopQueue = () => {
  return new Promise(resolve => 
    setImmediate(() => {
      console.log('Next please! How can I help you?');
      resolve();
    })
  );
}

// run while loop 
const run = async () => {
  while (!done) {
    console.log('I am busy! Doing some work: part #1'); 
    await eventLoopQueue();
    console.log('I am busy! Doing some work: part #1');
    await eventLoopQueue();
    console.log('I am busy! Doing some work: part #1');
    await eventLoopQueue();
  }
}

run().then(() => console.log('Done'));