Javascript, обещания и setTimeout - PullRequest
3 голосов
/ 05 мая 2020

Я играл с Promises, но у меня возникли проблемы с пониманием того, что происходит со следующим кодом:

const promise = new Promise((resolve, reject) => {
  console.log('Promise started - Async code started')
  setTimeout(() => {
    resolve('Success')
  }, 10)
})

setTimeout(() => {
  console.log('Promise log inside first setTimeout')
}, 0)

promise.then(res => {
  console.log('Promise log after fulfilled')
})

console.log('Promise made - Sync code terminated')

setTimeout(() => {
  console.log('Promise log inside second setTimeout')
}, 0)

Результат:


Promise started - Async code started 
Promise made - Sync code terminated 
Promise log inside first setTimeout 
Promise log inside second setTimeout 
Promise log after fulfilled

Как и ожидалось .

Но давайте проверим вывод приведенного ниже кода:

const promise = new Promise((resolve, reject) => {
  console.log('Promise started - Async code started')
  setTimeout(() => {
    resolve('Success')
  }, 1)
})

setTimeout(() => {
  console.log('Promise log inside first setTimeout')
}, 0)

promise.then(res => {
  console.log('Promise log after fulfilled')
})

console.log('Promise made - Sync code terminated')
setTimeout(() => {
  console.log('Promise log inside second setTimeout')
}, 0)

Изменено значение таймера setTimeout, которое должно быть разрешено, с 10 мс на 1 мс

Результат:

Promise started - Async code started 
Promise made - Sync code terminated 
Promise log after fulfilled 
Promise log inside first setTimeout 
Promise log inside second setTimeout 

Любое объяснение этому?

Ответы [ 3 ]

3 голосов
/ 05 мая 2020

From Модель параллелизма и событие l oop

  • setTimeout не запускается сразу после истечения его таймера
  • Нулевая задержка на самом деле не означает, что обратный вызов сработает через ноль миллисекунд. Вызов setTimeout с задержкой в ​​0 (ноль) миллисекунд не выполняет функцию обратного вызова после заданного интервала. По сути, setTimeout должен дождаться завершения всего кода для сообщений в очереди, даже если вы указали конкретный лимит времени для своего setTimeout.

Что происходит если мы установим 2 и 1 миллисекунды:

const promise = new Promise((resolve, reject) => {
  console.log('Promise started - Async code started')
  setTimeout(() => {
    resolve('Success')
  }, 2)
})

console.log('Promise log inside first setTimeout 1')
setTimeout(() => {
  console.log('Promise log inside first setTimeout 2')
}, 1)

promise.then(res => {
  console.log('Promise log after fulfilled ❌')

})

console.log('Promise log inside second setTimeout 1')
setTimeout(() => {
  console.log('Promise log inside second setTimeout 2')
}, 1)
});

Результат всегда будет:

Promise started - Async code started
Promise log inside first setTimeout 1
Promise log inside second setTimeout 1
Promise log inside first setTimeout 2
Promise log inside second setTimeout 2
Promise log after fulfilled ❌

Заключение

Если вы хотите правильного поведения, стоит избавиться нулевых задержек.

1 голос
/ 05 мая 2020

Я буду использовать следующий пример для объяснения:

setTimeout(() => {
  console.log('1 ms timeout');
}, 1);                            // Moved to async queue at time = T0
setTimeout(() => {
  console.log('0 ms timeout')
}, 0);                            // Moved to async queue after 1 ms that synchronous call to setTimeout takes i.e. at T1
                                  // So at T1, queue will be [("1ms timeout", 0), ("0ms timeout", 0)]

Следовательно, будет напечатано

1 ms timeout
0 ms timeout

Понимание приведенного выше: Вызов setTimeouts является синхронным (даже если его обратный вызов помещен в asyn c queue), т.е. мы вызываем setTimeout () и переходим к следующему оператору - само это синхронное действие может занять 1 мс.

Другими словами, 1 мс - это слишком мало времени, поэтому к моменту времени JS движок увидит второй асинхронный c оператор, первый из них уже потратил 1 мс в очереди.

Я также предлагаю вам попробовать следующее

setTimeout(() => {
  console.log("First");
}, 2);                      // queue at T0 = [("First", 2)]

const forLoopLimit = 100;
for (var i = 0; i < forLoopLimit; i++){
    console.log(i * 10000);
}                           // Assume that it takes about 3 milliseconds
                            // queue at T3 = [("First", 0)]
setTimeout(() => {
  console.log("Second");
}, 0);                      // Assume it takes 0 milliseconds.
                            // queue at T4 = [("First", 0), ("Second", 0)]

Это напечатает First до Second, даже если у первого был тайм-аут 2 мс по сравнению с последним, имеющим 0 мс. Теперь измените forLoopLimit на 1 или даже 10, вы увидите, что синхронная задача теперь не занимает 3 миллисекунды, а Second печатается перед First

Также стоит попробовать:

console.log(Date.now());
console.log(Date.now());

Попробуйте несколько раз выше, и вы увидите, что иногда журналы консоли имеют разные временные метки. Грубо говоря, console.log() и Date.now() занимают 0,5 мс. Это не что иное, как время для вызова / выполнения синхронного материала.

0 голосов
/ 10 июля 2020

Chrome имеет жестко запрограммировано минимальное время ожидания 1 мс .

  base::TimeDelta interval_milliseconds =
      std::max(base::TimeDelta::FromMilliseconds(1), interval);

Таким образом, для Chrome все ваши setTimeout( fn , 0 ) преобразуются в setTimeout( fn , 1 ) и, таким образом, запланировано срабатывание после первого, которое вы запланировали (помните, что конструкторы Promise вызываются синхронно).

setTimeout( () => console.log( '1ms delay' ), 1 );
setTimeout( () => console.log( '0ms delay' ), 0 );

и в Chrome, задержка 1ms всегда срабатывает первой, вопреки здравому смыслу, потому что внутренне это действительно будет:

setTimeout( () => console.log( '1ms delay' ), Math.max(1, 1) );
setTimeout( () => console.log( '0ms delay' ), Math.max(1, 0) );

Если бы вы установили 1 и 2 вместо 0 и 1, ваши ожидания были бы выполнены.

const promise = new Promise((resolve, reject) => {
  console.log('Promise started - Async code started')
  setTimeout(() => {
    resolve('Success')
  }, 2)
})

setTimeout(() => {
  console.log('Promise log inside first setTimeout')
}, 1)

promise.then(res => {
  console.log('Promise log after fulfilled')
})

console.log('Promise made - Sync code terminated')
setTimeout(() => {
  console.log('Promise log inside second setTimeout')
}, 1)
...