簡體   English   中英

Javascript,承諾和 setTimeout

[英]Javascript, Promises and setTimeout

我一直在玩 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)

output 是:


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

正如預期的那樣。

但是讓我們檢查以下代碼的 output:

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)

將要解決的 promise setTimeout 計時器值從 10ms 更改為 1ms

output 是:

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

對此有何解釋?

並發 model 和事件循環

  • setTimeout在其計時器到期后不會立即運行
  • 零延遲實際上並不意味着回調將在零毫秒后觸發。 以 0(零)毫秒的延遲調用setTimeout不會在給定時間間隔后執行回調 function。 基本上,即使您為 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)
});

output 始終為:

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 ❌

結論

如果你想要一個正確的行為,值得擺脫零延遲。

我將使用下面的例子來解釋:

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 是同步的(即使它的回調放在異步隊列中),即我們調用 setTimeout() 並移動到下一條語句 - 這個同步動作本身可能需要 1ms。

換句話說,1ms 太短了,所以當 JS 引擎看到第二個異步語句時,第一個已經在隊列中花費了 1ms。

我還建議您嘗試以下方法

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)]

即使前者的超時時間為 2 毫秒,而后者的超時時間為 0 毫秒,這也會在Second之前打印First 現在將forLoopLimit更改為 1 甚至 10,您會看到同步任務現在不需要 3 毫秒,並且在First之前打印了Second

另外值得一試的是:

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

多次嘗試以上,你會發現有時控制台日志會有不同的時間戳。 粗略地說,你可以說console.log()Date.now()需要 0.5 毫秒。 只是調用/執行同步內容的時間。

Chrome 的硬編碼最小超時為 1ms

  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) );

如果您將其設置為12而不是01 ,您的期望就會得到滿足。

 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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM