Heim  >  Artikel  >  Web-Frontend  >  Eine ausführliche Analyse, warum Promise schneller ist als setTimeout()

Eine ausführliche Analyse, warum Promise schneller ist als setTimeout()

青灯夜游
青灯夜游nach vorne
2021-02-04 16:04:401566Durchsuche

Warum ist Promise schneller als setTimeout()? Der folgende Artikel analysiert die Gründe für Sie. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird für alle hilfreich sein.

Eine ausführliche Analyse, warum Promise schneller ist als setTimeout()

Verwandte Empfehlungen: „Javascript-Video-Tutorial

1. Experimentieren

Lassen Sie uns ein Experiment durchführen. Was wird schneller ausgeführt: ein sofort aufgelöstes Promise oder ein sofortiges setTimeout (d. h. ein setTimeout von 0 Millisekunden)? setTimeout(也就是0毫秒的setTimeout)?

Promise.resolve(1).then(function resolve() {
  console.log('Resolved!');
});

setTimeout(function timeout() {
  console.log('Timed out!');
}, 0);

// 'Resolved!'
// 'Timed out!'

promise.resolve(1)是一个静态函数,它返回一个立即解析的promisesetTimeout(callback, 0)0毫秒的延迟执行回调函数。

我们可以看到先打印'Resolved!',再打印Timeout completed!,立即解决的 promise 比立即setTimeout更快。

是因为Promise.resolve(true).then(...)setTimeout(..., 0)之前被调用了,所以 Promise 过程会更快吗? 公平的问题。

所以,我们稍微更改一下实验条件,然后先调用setTimeout(..., 0)

setTimeout(function timeout() {
  console.log('Timed out!');
}, 0);

Promise.resolve(1).then(function resolve() {
  console.log('Resolved!');
});

// 'Resolved!'
// 'Timed out!'

setTimeout(..., 0)Promise.resolve(true).then(...)之前被调用。但,还是先打印Resolved!在打印'Timed out!'

这是为啥呢?

2.事件循环

与异步 JS 相关的问题可以通过研究事件循环来回答。我们回顾一下异步 JS 工作方式的主要组成部分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lt9zVHTf-1611275604640)(/img/bVcMQaI)]

调用堆栈是一个LIFO(后进先出)结构,它存储在代码执行期间创建的执行上下文。简单地说,调用堆栈执行这些函数。

Web api是异步操作(fetch 请求、promise、计时器)及其回调等待完成的地方。

**task queue (任务队列)是一个FIFO(先进先出)**结构,它保存准备执行的异步操作的回调。例如,超时的setTimeout()的回调函数或准备执行的单击按钮事件处理程序都在任务队列中排队。

**job queue (作业队列)**是一个FIFO(先入先出)结构,它保存准备执行的promise 的回调。例如,已完成的承诺的resolvereject回调被排在作业队列中。

最后,事件循环永久监听调用堆栈是否为空。如果调用堆栈为空,则事件循环查看作业队列或任务队列,并将准备执行的任何回调分派到调用堆栈中。

3.作业队列与任务队列

我们从事件循环的角度来看这个实验,我将对代码执行进行一步一步的分析。

A)调用堆栈执行setTimeout(..., 0)并计划一个计时器, timeout()回调存储在Web API中:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SLk0AUa5-1611275604642)(/img/bVcMQdg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zr7usYTK-1611275604643)(/img/bVcMQc9)]

B)调用堆栈执行 Promise.resolve(true).then(resolve)并安排一个 promise 解决方案。 resolved()回调存储在Web API中:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JTwSnLYS-1611275604646)(/img/bVcMQdh)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k5cRhqzN-1611275604648)(/img/bVcMQdi)]

C)promise 立即被解析,同时计时器也立即执行。这样,定时器回调timeout()进入任务队列,promise回调resolve()进入作业队列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iMfLB2YJ-1611275604649)(/img/bVcMQdS)]

D)现在是有趣的部分:作业队列(微任务)优先级高于任务队列(宏任务)。 事件循环从作业队列中取出promise回调resolve()并将其放入调用堆栈中。 然后,调用堆栈执行promise回调resolve()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nnqfgoo1-1611275604650)(/img/bVcMQey)]

E)最后,事件循环将计时器回调timeout()从任务队列中出队到调用堆栈中。 然后,调用堆栈执行计时器回调timeout()rrreee

promise.resolve(1) ist eine statische Funktion Es gibt ein promise zurück, das sofort aufgelöst wird. setTimeout(callback, 0) führt die Callback-Funktion mit einer Verzögerung von 0 Millisekunden aus.

Wir können sehen, dass das Drucken von 'Resolved!' zuerst und dann das Drucken von Timeout abgeschlossen! das Versprechen, das sofort gelöst wird, schneller ist als das unmittelbare setTimeout Code>. 🎜🎜Das liegt daran, dass <code>Promise.resolve(true).then(...) vor setTimeout(..., 0) aufgerufen wird, sodass der Promise-Prozess aktualisiert wird Ist es schnell? Berechtigte Frage. 🎜🎜Also ändern wir die experimentellen Bedingungen leicht und rufen dann zuerst setTimeout(..., 0) auf: 🎜rrreee🎜setTimeout(..., 0) in Promise.resolve(true).then(...) wurde bereits zuvor aufgerufen. Allerdings wird zuerst Resolved! und dann 'Timed out!' ausgegeben. 🎜🎜Warum ist das so? 🎜🎜🎜2. Ereignisschleife 🎜🎜🎜Fragen im Zusammenhang mit asynchronem JS können durch das Studium der Ereignisschleife beantwortet werden. Sehen wir uns die Hauptkomponenten der Funktionsweise von asynchronem JS an. 🎜🎜[Externer Link Bildübertragung fehlgeschlagen, die Quellseite verfügt möglicherweise über einen Anti-Leeching-Mechanismus, es wird empfohlen, das Bild zu speichern und direkt hochzuladen (img-Lt9zVHTf-1611275604640)(/img/bVcMQaI)]🎜🎜🎜Der Aufruf stack🎜 ist eine LIFO-Struktur (Lagging In First Out), die den während der Codeausführung erstellten Ausführungskontext speichert. Einfach ausgedrückt führt der Aufrufstapel diese Funktionen aus. 🎜🎜In der Web-API warten asynchrone Vorgänge (Abrufanforderungen, Versprechen, Timer) und ihre Rückrufe auf den Abschluss. 🎜🎜**Aufgabenwarteschlange 🎜 ist eine 🎜FIFO-Struktur (First In, First Out)**, die Rückrufe für asynchrone Vorgänge speichert, die zur Ausführung bereit sind. Beispielsweise wird eine Rückruffunktion, bei der eine Zeitüberschreitung auftritt, oder ein Ereignishandler für Schaltflächenklicks, der zur Ausführung bereit ist, in der Aufgabenwarteschlange eingereiht. 🎜🎜**Job-Warteschlange** ist eine FIFO-Struktur (First In, First Out), die die Rückrufe von promise speichert, die zur Ausführung bereit sind. Beispielsweise wird der resolve- oder reject-Rückruf eines abgeschlossenen Versprechens in die Jobwarteschlange eingereiht. 🎜🎜Abschließend überwacht die Ereignisschleife permanent, ob der Aufrufstapel leer ist. Wenn der Aufrufstapel leer ist, prüft die Ereignisschleife die Jobwarteschlange oder Aufgabenwarteschlange und sendet alle Rückrufe, die zur Ausführung bereit sind, an den Aufrufstapel. 🎜🎜🎜3. Jobwarteschlange und Aufgabenwarteschlange🎜🎜🎜 Schauen wir uns dieses Experiment aus der Perspektive der Ereignisschleife an und ich werde die Codeausführung Schritt für Schritt analysieren. 🎜🎜A) Der Aufrufstapel führt setTimeout(..., 0) aus und plant einen Timer. Der timeout()-Rückruf wird in der Web-API gespeichert: 🎜🎜[ Externer Link Die Bildübertragung ist möglicherweise über einen Anti-Hotlink-Mechanismus verfügt. Es wird empfohlen, das Bild zu speichern und direkt hochzuladen (img-SLk0AUa5-1611275604642)(/img/bVcMQdg)]🎜🎜Das Bild mit dem externen Link Übertragung fehlgeschlagen. Die Quellseite verfügt möglicherweise über einen Anti-Hotlink-Mechanismus. Es wird empfohlen, das Bild zu speichern und direkt hochzuladen (img-Zr7usYTK-1611275604643)(/img/bVcMQc9)]🎜🎜B) code>Promise.resolve(true).then(resolve) und arrangiert eine promise-Lösung. resolved() Der Rückruf wird in der Web-API gespeichert: 🎜🎜[Die Bildübertragung über den externen Link ist fehlgeschlagen. Die Quellseite verfügt möglicherweise über einen Anti-Leeching-Mechanismus. Es wird empfohlen, das Bild zu speichern und hochzuladen direkt (img-JTwSnLYS-1611275604646)( /img/bVcMQdh)]🎜🎜Die Quellseite verfügt möglicherweise über einen Anti-Leeching-Mechanismus. Es wird empfohlen, das Bild zu speichern und direkt hochzuladen (img-). k5cRhqzN-1611275604648)(/img/bVcMQdi)]🎜🎜C)promise Es wird sofort analysiert und der Timer wird sofort ausgeführt. Auf diese Weise gelangt der Timer-Rückruf timeout() in die Aufgabenwarteschlange und der promise-Rückruf resolve() in die Jobwarteschlange🎜🎜[ Die Bildübertragung des externen Links ist fehlgeschlagen. Die Quellseite verfügt möglicherweise über einen Anti-Leeching-Mechanismus. Es wird empfohlen, das Bild zu speichern und direkt hochzuladen (img-iMfLB2YJ-1611275604649)(/img/bVcMQdS)]🎜🎜D) Jetzt kommt das Interessante Teil: Die Priorität der Jobwarteschlange (Mikrotask) ist höher als die der Taskwarteschlange (Makrotask). Die Ereignisschleife entnimmt den versprochenen Rückruf resolve() aus der Jobwarteschlange und legt ihn auf dem Aufrufstapel ab. Anschließend führt der Aufrufstapel den Promise-Callback resolve() aus: 🎜🎜[Die Bildübertragung über den externen Link ist fehlgeschlagen. Die Quellseite verfügt möglicherweise über einen Anti-Leeching-Mechanismus. Es wird empfohlen, das Bild zu speichern und hochzuladen es direkt (img-nnqfgoo1-1611275604650) (/img/bVcMQey)]🎜🎜E) Schließlich entfernt die Ereignisschleife den Timer-Rückruf timeout() aus der Aufgabenwarteschlange auf den Aufrufstapel. Anschließend führt der Aufrufstapel den Timer-Rückruf timeout() aus: 🎜🎜[Die Bildübertragung über den externen Link ist fehlgeschlagen. Die Quellseite verfügt möglicherweise über einen Anti-Leeching-Mechanismus. Es wird empfohlen, das Bild zu speichern und hochzuladen es direkt (img-Fj54WaI0-1611275604650 )(/img/bVcMQeB)]🎜

Der Aufrufstapel ist leer und die Ausführung des Skripts ist abgeschlossen.

Zusammenfassung

Warum werden sofort gelöste Versprechen schneller verarbeitet als Timer für die sofortige Ausführung?

Aufgrund der Prioritäten der Ereignisschleife hat die Aufgabenwarteschlange (in der der setTimeout()回调)相比,作业队列(用于存储已实现的PromiseRückruf für Zeitüberschreitungen gespeichert ist) eine höhere Priorität.

Originaladresse: https://dmitripavlutin.com/javascript-promises-settimeout/

Autor: Milos Protic

Übersetzungsadresse: https://segmentfault.com/a/1190000038769853

Mehr Computerprogrammierung für Weitere Informationen zum entsprechenden Thema finden Sie unter: Programmiervideo! !

Das obige ist der detaillierte Inhalt vonEine ausführliche Analyse, warum Promise schneller ist als setTimeout(). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen