почему setTimeout не вызывается, когда вызывается из бесконечного цикла while - PullRequest
0 голосов
/ 10 декабря 2018

Я столкнулся с забавной проблемой, я пытался вызвать функцию (которая имеет setTimeout в своем теле) из бесконечного цикла, и он никогда не вызывался, но когда цикл был изменен на не бесконечный, вызов былпроисходит !!

это работает: -

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 голосов
/ 21 декабря 2018

как вызвать функцию с 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'));
0 голосов
/ 10 декабря 2018

Так работает цикл обработки событий в javascript.Обратные вызовы setTimeout () добавляются в очередь задач макроса.Очередь макрозадач обрабатывается только тогда, когда ни один другой JavaScript не выполняется в середине, или когда стек вызовов пуст.Таким образом, обратные вызовы setTimeout() не могут выполняться до тех пор, пока цикл while (и код, который он содержит) не завершится и не вернет управление обратно в браузер.

Так в примере 1:

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

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

Выполнение while завершается и возвращает управление обратно в браузер (callstack пуст), поэтому все обратные вызовы setTimeout() выполняются один за другим.

, а в example2:

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

while (true) {
  bar();
}

itявляется бесконечным циклом, поэтому управление никогда не возвращается в браузер или стек вызовов никогда не бывает пустым, поэтому цикл обработки событий не может обработать очередь макросов, поэтому обратные вызовы setTimeout() никогда не выполняются.

0 голосов
/ 10 декабря 2018

Давайте сначала посмотрим, как этот (бесконечный) код выполняется

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();
0 голосов
/ 10 декабря 2018

Это потому, что ваш код JavaScript является однопоточным.Бесконечный цикл while сохраняет браузер занятым, и у него нет времени на выполнение других асинхронных функций, включая вашу bar функцию.

0 голосов
/ 10 декабря 2018

Это из-за того, как работает цикл обработки событий.Асинхронные события, такие как тайм-ауты, ставятся в очередь и обрабатываются после основной части вашего скрипта выполняется до завершения .Это означает, что он ожидает, пока ваш цикл while завершится, прежде чем начнет выравниваться обратный вызов setTimeout.Это одна (из нескольких) причин, почему вы не хотите блокировать поток с помощью долговременного синхронного кода, такого как гигантские циклы в javascript.Ничего другого не может произойти, пока вращается ваш бесконечный цикл while.

...