이벤트 루프 컨텍스트 내에서 마이크로 태스크와 매크로 태스크의 차이점


140

방금 Promises / A + 사양을 읽었으며 마이크로 태스크 및 매크로 태스크라는 용어를 발견했습니다. http://promisesaplus.com/#notes

이전에이 용어에 대해 들어 본 적이 없으며 이제 차이점이 무엇인지 궁금합니다.

나는 이미 웹에서 일부 정보를 찾으려고 노력했지만, 내가 찾은 것은 w3.org Archives (이 차이점을 설명하지는 않음) 의이 게시물입니다 .http : //lists.w3.org/Archives /Public/public-nextweb/2013Jul/0018.html

또한 "macrotask"라는 npm 모듈을 찾았습니다. https://www.npmjs.org/package/macrotask 또한 차이점이 정확히 무엇인지는 명확하지 않습니다.

내가 아는 것은 https://html.spec.whatwg.org/multipage/webappapis.html#task-queuehttps : //html.spec.whatwg에 설명 된 것처럼 이벤트 루프와 관련이 있다는 것입니다 . .org / multipage / webappapis.html # perform-a-microtask-checkpoint

이 WHATWG 사양을 감안할 때 이론적으로 차이를 스스로 추출 할 수 있어야한다는 것을 알고 있습니다. 그러나 다른 사람들도 전문가의 짧은 설명으로 혜택을 볼 수 있다고 확신합니다.


간단히 말해서 : 여러 개의 중첩 된 이벤트 큐. 직접 구현할 수도 있습니다.while (task = todo.shift()) task();
Bergi

1
좀 더 자세한 내용을 원하시는 분께 : JavaScript Ninja의 비밀, 2 판, 13 장 생존 이벤트
Ethan

답변:


220

이벤트 루프의 한 번의 둘러보기매크로 태스크 큐 에서 정확히 하나의 태스크를 처리합니다 (이 큐는 단순히 WHATWG 스펙 에서 태스크 큐 라고 함 ). 이 매크로 태스크가 완료되면 사용 가능한 모든 마이크로 태스크 가 처리됩니다 (즉, 동일한 처리주기 내에서). 이러한 마이크로 태스크는 처리되는 동안 마이크로 태스크 큐가 소진 될 때까지 더 많은 마이크로 태스크를 큐에 넣을 수 있습니다.

이것의 실제적인 결과는 무엇입니까?

마이크로 태스크가 다른 마이크로 태스크를 재귀 적으로 큐 에 넣는 경우 다음 매크로 태스크가 처리 될 때까지 시간이 오래 걸릴 수 있습니다. 즉, UI가 차단되거나 응용 프로그램에서 일부 유휴 I / O 유휴 상태가 될 수 있습니다.

그러나 적어도 Node.js의 process.nextTick 함수 ( 마이크로 태스크 를 대기열에 넣는 ) 와 관련하여 process.maxTickDepth 를 통해 이러한 차단에 대한 보호 기능이 내장되어 있습니다. 이 값은 기본값 인 1000으로 설정 되어이 한계에 도달 한 후 다음 매크로 태스크 를 처리 할 수있는 마이크로 태스크의 추가 처리를 줄입니다.

그래서 언제 무엇을 사용해야합니까?

기본적으로 비동기 방식으로 작업을 수행 해야하는 경우 (즉 , 가장 가까운 미래에이 (마이크로) 작업을 수행 한다고 말할 때 ) 마이크로 태스크를 사용하십시오 . 그렇지 않으면 macrotasks고수하십시오 .

매크로 작업 : setTimeout , setInterval , setImmediate , requestAnimationFrame , I / O , UI 렌더링
마이크로 태스크 : process.nextTick , Promises , queueMicrotask , MutationObserver


4
이벤트 루프에는 마이크로 태스크 체크 포인트가 있지만 대부분의 개발자가 마이크로 태스크를 접하게되는 곳은 아닙니다. JS 스택이 비면 마이크로 태스크가 처리됩니다. 이것은 작업 내에서 또는 이벤트 루프의 렌더링 단계 내에서 여러 번 발생할 수 있습니다.
JaffaTheCake

2
process.maxTickDepth오래 전에 제거되었습니다 : github.com/nodejs/node/blob/…
RidgeA

queueMicrotask () 메소드를 사용하여 새로운 마이크로 태스크를 추가 할 수도 있습니다
ZoomAll

감사합니다 @ZoomAll, 지금까지 queueMicrotask ()를 몰랐습니다. 나는 모든 것들에 대한 답변과 링크에 답변을 추가했습니다 ...
NicBright

requestAnimationFrame (rAF)은 마이크로 태스크를 생성 할뿐만 아니라 일반적으로 rAF 호출은 별도의 대기열을
ZoomAll

67

사양의 기본 개념 :

  • 이벤트 루프에는 하나 이상의 작업 대기열이 있습니다 (작업 대기열은 매크로 작업 대기열입니다).
  • 각 이벤트 루프에는 마이크로 태스크 대기열이 있습니다.
  • 작업 대기열 = 매크로 작업 대기열! = 마이크로 작업 대기열
  • 작업이 매크로 작업 대기열 또는 마이크로 작업 대기열로 푸시 될 수 있습니다
  • 작업이 대기열 (마이크로 / 매크로)로 푸시되면 작업 준비가 완료되었음을 의미하므로 작업을 지금 실행할 수 있습니다.

이벤트 루프 프로세스 모델은 다음과 같습니다.

호출 스택이 비어의 steps-을

  1. 작업 대기열에서 가장 오래된 작업 (작업 A)을 선택하십시오.
  2. 작업 A가 null 인 경우 (작업 대기열이 비어 있음을 의미) 6 단계로 건너 뜁니다.
  3. "현재 실행중인 작업"을 "작업 A"로 설정
  4. "태스크 A"실행 (콜백 함수 실행을 의미)
  5. "현재 실행중인 작업"을 null로 설정하고 "작업 A"를 제거하십시오.
  6. 마이크로 태스크 대기열 수행
    • (a). 마이크로 작업 대기열에서 가장 오래된 작업 (작업 x)을 선택합니다
    • (b). 작업 x가 null 인 경우 (마이크로 작업 대기열이 비어 있음을 의미) 단계 (g)로 점프
    • (c). "현재 실행중인 작업"을 "task x"로 설정
    • (d) .run "작업 x"
    • (e). "현재 실행중인 작업"을 null로 설정하고 "task x"를 제거하십시오.
    • (f). 마이크로 태스킹 대기열에서 다음으로 오래된 작업을 선택하고 단계 (b)로 이동
    • (g). 마무리 마이크로 태스크 큐;
  7. 1 단계로 이동하십시오.

단순화 된 프로세스 모델은 다음과 같습니다.

  1. 매크로 작업 대기열에서 가장 오래된 작업을 실행 한 다음 제거하십시오.
  2. 사용 가능한 모든 작업을 마이크로 태스킹 대기열에서 실행 한 다음 제거하십시오.
  3. 다음 라운드 : 매크로 작업 대기열에서 다음 작업 실행 (점프 2 단계)

기억해야 할 것 :

  1. 매크로 작업 대기열에있는 작업이 실행 중이면 새로운 이벤트가 등록 될 수 있으므로 새로운 작업이 생성 될 수 있습니다.
    • promiseA.then ()의 콜백은 작업입니다
      • promiseA가 해결 / 거부 됨 : 현재 이벤트 루프 라운드에서 작업이 마이크로 태스크 대기열로 푸시됩니다.
      • promiseA 보류 중 : 향후 이벤트 루프 라운드에서 작업이 마이크로 태스크 대기열로 푸시됩니다 (다음 라운드 일 수 있음)
    • setTimeout (callback, n)의 콜백은 작업이며, n이 0 인 매크로 작업 대기열로 푸시됩니다.
  2. 마이크로 태스킹 대기열의 작업은 현재 라운드에서 실행되는 반면, 매크로 태스크 대기열의 작업은 다음 라운드의 이벤트 루프를 기다려야합니다.
  3. 우리는 모두 "클릭", "스크롤", "ajax", "setTimeout"의 콜백을 알고 있습니다. 그러나 스크립트 태그에서 전체적으로 js 코드도 기억해야합니다 (매크로 태스크).

2
이것은 훌륭한 설명입니다! 공유해 주셔서 감사합니다!. 언급에 한가지 더에 NodeJs , setImmediate()매크로 / 작업이며, process.nextTick()마이크로 / 일이다.
LeOn-한 리

6
브라우저 paint작업은 어떻습니까? 어떤 카테고리에 적합합니까?
Legends

나는 그들이 (같은 마이크로 작업에 들어갈 것이라고 생각 requestAnimationFrame)
Divyanshu Maithani

v8 이벤트 루프가 실행되는 순서는 다음과 같습니다-> Call Stack || 마이크로 작업 || 작업 대기열 || rAF || 렌더 트리 || 레이아웃 || 페인트 || <화면에 픽셀을 그리기위한 OS 기본 호출> <----- 1) DOM (새로운 변경 사항), CSSOM (새로운 변경 사항), 렌더 트리, 레이아웃 및 페인트는 이벤트 루프 타이머에 따라 requestAnimationFrame 콜백 후에 발생합니다. 따라서 rAF 전에 DOM 작업을 완료하고 나머지는 rAF로 진행할 수 있어야합니다. PS : rAF를 호출하면 매크로 작업 실행이 트리거됩니다.
Anvesh Checka

내가 실수인지는 모르겠지만이 답변에 동의하지 않으면 마이크로 태스크가 매크로 작업보다 먼저 실행됩니다. codepen.io/walox/pen/yLYjNRq ?
walox

9

스택과 분리하여 이벤트 루프에 대해 논의 할 수 없다고 생각합니다.

JS에는 세 개의 "스택"이 있습니다.

  • 모든 동기 호출에 대한 표준 스택 (한 함수가 다른 함수를 호출하는 등)
  • 우선 순위가 높은 모든 비동기 작업 (process.nextTick, Promises, Object.observe, MutationObserver)에 대한 마이크로 태스크 대기열 (또는 작업 큐 또는 마이크로 태스크 스택 )
  • 우선 순위가 낮은 모든 비동기 작업 (setTimeout, setInterval, setImmediate, requestAnimationFrame, I / O, UI 렌더링)에 대한 매크로 작업 대기열 (또는 이벤트 대기열, 작업 대기열, 매크로 작업 대기열 )
|=======|
| macro |
| [...] |
|       |
|=======|
| micro |
| [...] |
|       |
|=======|
| stack |
| [...] |
|       |
|=======|

그리고 이벤트 루프는 다음과 같이 작동합니다.

  • 스택에서 맨 아래부터 맨 위까지 모든 것을 실행하고 스택이 비어있을 때만 위의 대기열에서 무슨 일이 일어나고 있는지 확인하십시오.
  • 마이크로 스택을 확인하고 스택의 도움으로 모든 작업을 실행하십시오 (필요한 경우). 마이크로 작업 대기열이 비어 있거나 실행이 필요하지 않을 때까지 한 번에 한 번의 작업으로 매크로 스택을 확인하십시오.
  • 매크로 스택을 확인하고 스택의 도움으로 필요한 경우 모든 것을 실행하십시오.

스택이 비어 있지 않으면 Mico 스택이 닿지 않습니다. 마이크로 스택이 비어 있지 않거나 실행이 필요하지 않으면 매크로 스택은 건드리지 않습니다.

요약하자면, 마이크로 태스킹 큐는 매크로 태스크 큐와 거의 동일하지만 해당 태스크 (process.nextTick, Promises, Object.observe, MutationObserver)매크로 태스크 보다 우선 순위가 높습니다.

마이크로는 매크로와 비슷하지만 우선 순위가 높습니다.

여기에 모든 것을 이해하기위한 "궁극적 인"코드가 있습니다.

console.log('stack [1]');
setTimeout(() => console.log("macro [2]"), 0);
setTimeout(() => console.log("macro [3]"), 1);

const p = Promise.resolve();
for(let i = 0; i < 3; i++) p.then(() => {
    setTimeout(() => {
        console.log('stack [4]')
        setTimeout(() => console.log("macro [5]"), 0);
        p.then(() => console.log('micro [6]'));
    }, 0);
    console.log("stack [7]");
});

console.log("macro [8]");

/* Result:
stack [1]
macro [8]

stack [7], stack [7], stack [7]

macro [2]
macro [3]

stack [4]
micro [6]
stack [4]
micro [6]
stack [4]
micro [6]

macro [5], macro [5], macro [5]
--------------------
but in node in versions < 11 (older versions) you will get something different


stack [1]
macro [8]

stack [7], stack [7], stack [7]

macro [2]
macro [3]

stack [4], stack [4], stack [4]
micro [6], micro [6], micro [6]

macro [5], macro [5], macro [5]

more info: https://blog.insiderattack.net/new-changes-to-timers-and-microtasks-from-node-v11-0-0-and-above-68d112743eb3
*/

1
큐를 호출하는 것은 완전히 혼란 스럽다.
Bergi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.