>  기사  >  웹 프론트엔드  >  Promise가 setTimeout()보다 빠른 이유에 대한 심층 분석

Promise가 setTimeout()보다 빠른 이유에 대한 심층 분석

青灯夜游
青灯夜游앞으로
2021-02-04 16:04:401566검색

Promise가 setTimeout()보다 빠른 이유는 무엇입니까? 다음 기사에서는 그 이유를 분석해 보겠습니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.

Promise가 setTimeout()보다 빠른 이유에 대한 심층 분석

관련 추천: "javascript 비디오 튜토리얼"

1. 실험

실험을 해보자. 어느 것이 더 빠르게 실행됩니까? 즉시 해결된 Promise 또는 즉시 setTimeout(즉, 0밀리초의 setTimeout) 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)은 정적 함수입니까? , 즉시 해결되는 promise를 반환합니다. setTimeout(callback, 0)0밀리초의 지연으로 콜백 함수를 실행합니다.

먼저 'Resolved!'를 인쇄한 다음 Timeoutcomplete!를 인쇄하는 것을 볼 수 있습니다. 즉시 해결되는 Promise는 즉각적인 setTimeout보다 빠릅니다. 코드>. 🎜🎜setTimeout(..., 0)보다 먼저 Promise.resolve(true).then(...)이 호출되므로 Promise 프로세스가 업데이트되기 때문입니다. 빠른가? 공정한 질문입니다. 🎜🎜그래서 실험 조건을 약간 변경한 다음 setTimeout(..., 0)을 먼저 호출합니다. 🎜rrreee🎜setTimeout(..., 0) Promise.resolve(true).then(...)은 이전에 호출되었습니다. 그러나 Resolved!가 먼저 인쇄된 다음 'Timed out!'이 인쇄됩니다. 🎜🎜이게 왜죠? 🎜🎜🎜2. 이벤트 루프 🎜🎜🎜 비동기 JS와 관련된 질문은 이벤트 루프를 연구하여 답할 수 있습니다. 비동기식 JS 작동 방식의 주요 구성 요소를 검토해 보겠습니다. 🎜🎜[외부 링크 이미지 전송에 실패했습니다. 소스 사이트에 리칭 방지 메커니즘이 있을 수 있습니다. 이미지를 저장하고 직접 업로드하는 것이 좋습니다(img-Lt9zVHTf-1611275604640)(/img/bVcMQaI)]🎜🎜🎜통화 stack🎜은 코드 실행 중에 생성된 실행 컨텍스트를 저장하는 LIFO(Lagged In First Out) 구조입니다. 간단히 말해서 호출 스택은 이러한 기능을 실행합니다. 🎜🎜웹 API는 비동기 작업(요청, 약속, 타이머 가져오기)과 해당 콜백이 완료될 때까지 기다리는 곳입니다. 🎜🎜**작업 대기열 🎜은 실행 준비가 된 비동기 작업에 대한 콜백을 저장하는 🎜FIFO(선입선출)** 구조입니다. 예를 들어 시간 초과된 콜백 함수나 실행할 준비가 된 버튼 클릭 이벤트 핸들러는 작업 대기열에 대기합니다. 🎜🎜**작업 대기열**은 실행할 준비가 된 promise의 콜백을 저장하는 FIFO(선입선출) 구조입니다. 예를 들어 완료된 약속의 resolve 또는 reject 콜백은 작업 대기열에 포함됩니다. 🎜🎜마지막으로 이벤트 루프는 호출 스택이 비어 있는지 영구적으로 청취합니다. 호출 스택이 비어 있으면 이벤트 루프는 작업 대기열 또는 작업 대기열을 살펴보고 실행할 준비가 된 모든 콜백을 호출 스택으로 전달합니다. 🎜🎜🎜3. Job Queue 및 Task Queue🎜🎜🎜 이 실험을 이벤트 루프의 관점에서 살펴보고, 코드 실행을 단계별로 분석하겠습니다. 🎜🎜A) 호출 스택은 setTimeout(..., 0)을 실행하고 타이머를 예약합니다. timeout() 콜백은 웹 API에 저장됩니다: 🎜🎜[ 외부 링크 이미지 전송에 실패했습니다. 원본 사이트에 핫링크 방지 메커니즘이 있을 수 있습니다. 이미지를 저장하고 직접 업로드하는 것이 좋습니다(img-SLk0AUa5-1611275604642)(/img/bVcMQdg)]🎜🎜[외부 링크 이미지 전송이 실패했습니다. 소스 사이트에 핫링크 방지 메커니즘이 있을 수 있으므로 이미지를 저장하고 직접 업로드하는 것이 좋습니다(img-Zr7usYTK-1611275604643)(/img/bVcMQc9)]🎜🎜B) 호출 스택이 Promise.resolve(true).then(resolve) 및 promise 솔루션을 준비합니다. resolved() 콜백은 웹 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()을 실행합니다. 🎜🎜[외부 링크 이미지 전송에 실패했습니다. 소스 사이트에 안티 리칭 메커니즘이 있을 수 있습니다. 이미지를 저장하고 업로드하는 것이 좋습니다. 직접 (img-Fj54WaI0-1611275604650 )(/img/bVcMQeB)]🎜

콜 스택이 비어 있고 스크립트 실행이 완료되었습니다.

Summary

즉시 해결 약속이 즉시 실행 타이머보다 빠르게 처리되는 이유는 무엇입니까?

이벤트 루프 우선순위로 인해 작업 대기열(시간 초과에 대한 setTimeout()回调)相比,作业队列(用于存储已实现的Promise콜백을 저장함)의 우선순위가 더 높습니다.

원본 주소: https://dmitripavlutin.com/javascript-promises-settimeout/

저자: Milos Protic

번역 주소: https://segmentfault.com/a/1190000038769853

더 많은 컴퓨터 프로그래밍 관련 지식이 있으면 프로그래밍 비디오를 방문하세요! !

위 내용은 Promise가 setTimeout()보다 빠른 이유에 대한 심층 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제