이 글은 JavaScript 브라우저의 이벤트 루프 메커니즘에 대한 자세한 설명을 제공합니다. 필요한 친구가 참고할 수 있기를 바랍니다.
앞으로의 질문:
단일 스레드는 어떻게 비동기 성능을 달성하나요?
이벤트 루프의 프로세스는 무엇인가요?
매크로태스크와 마이크로태스크는 무엇이며, 이들의 차이점은 무엇인가요?
단일 스레드와 비동기
js에 대해 언급했는데, 단일 스레드와 비동기식을 생각할 것인데, 단일 스레드가 어떻게 비동기성을 달성합니까? 개념 먼저, 먼저 단일 스레드와 비동기 간의 관계를 이해해야 합니다.
JS 작업은 동기식과 비동기식의 두 가지 유형으로 나뉘며, 처리 방법도 다릅니다. 동기식 작업은 실행을 위해 메인 스레드에 직접 대기하는 반면, 비동기식 작업은 작업 대기열에 배치됩니다. (비동기 작업) 작업 대기열에 대기해야 합니다. 작업 대기열은 버퍼와 같습니다. 작업은 다음으로 호출 스택(호출 스택)으로 이동한 다음 주 스레드가 해당 작업을 실행합니다. 호출 스택.
단일 스레드는 js 코드 구문 분석 및 실행을 담당하는 js 엔진에 단 하나의 스레드(메인 스레드)만 있음을 의미합니다. 즉, 한 번에 한 가지 작업만 수행할 수 있으며 Ajax 요청에 대해 이를 알고 있습니다. , 메인 스레드는 응답을 기다립니다. 다른 작업을 수행하기 위해 브라우저는 먼저 이벤트 테이블에 ajax 콜백 함수를 등록합니다. 응답이 돌아온 후 콜백 함수가 작업 대기열에 추가되고 실행을 기다립니다. 스레드 차단을 일으키지 않으므로 js가 ajax 요청을 처리하는 방식은 비동기식입니다.
간단히 말하면, 콜 스택이 비어 있는지 확인하고 콜 스택에 어떤 작업을 추가할지 결정하는 과정이 이벤트 루프이며, js의 비동기 구현의 핵심이 이벤트 루프입니다.
콜 스택 및 작업 대기열
이름에서 알 수 있듯이 콜 스택은 스택 구조입니다. 함수 호출은 현재 실행 중인 함수의 매개 변수 및 로컬 변수와 같은 상황별 정보를 포함합니다. 실행되면 해당 컨텍스트가 스택에서 팝됩니다.
아래 그림은 콜스택과 태스크 큐의 관계도입니다
이벤트 루프
이벤트 루프에 대하여, HTML 스펙 소개
사용자 에이전트당 하나 이상의 이벤트 루프가 있어야 합니다 , 그리고 기껏해야
관련 유사 출처 브라우징 컨텍스트 단위당 하나의 이벤트 루프.
이벤트 루프에는 하나 이상의 작업 대기열이 있습니다.
각 작업은 특정 작업 소스에서 오는 것으로 정의됩니다.
사양에 따르면 브라우저에는 적어도 하나의 이벤트가 있습니다. loop 의 경우, 이벤트 루프에는 하나 이상의 작업 대기열(매크로태스크)이 있고, 각 외부 작업에는 자체 그룹이 있으며, 브라우저는 다양한 작업 그룹에 대한 우선순위를 설정합니다.
macrotask & microtask
사양에는 두 가지 개념이 언급되어 있지만 자세히 소개하지는 않습니다. 몇 가지 정보를 참고한 후 다음과 같이 요약할 수 있습니다.
macrotask: js 코드의 전체 실행, 이벤트 콜백, XHR 콜백이 포함됩니다. , 타이머(setTimeout/ setInterval/setImmediate), IO 작업, UI render
microtask: Promise 콜백, MutationObserver, process.nextTick, Object.observe
를 포함한 애플리케이션 상태 업데이트 작업. 여기서 setImmediate 및 process.nextTick은 다음을 구현합니다. nodejs에 대해서는 nodejs 기사에서 자세히 소개하겠습니다.
이벤트 처리 과정
매크로태스크와 마이크로태스크의 이해는 이렇게만 보면 다소 모호할 수 있지만, 이벤트 루프의 메커니즘과 결합하면 훨씬 더 명확해집니다. 매우 명확한 소개라고 할 수 있습니다.
요약하자면 이벤트 루프의 단계는 다음과 같습니다.
1. 매크로태스크 대기열이 비어 있는지 확인하고, 비어 있지 않으면 2로 이동하고, 비어 있으면 2로 이동합니다. 3
2. 매크로태스크에서 작업을 실행합니다. 3. 계속해서 마이크로태스크 대기열이 비어 있는지 확인하고, 비어 있으면 4로 이동하고, 그렇지 않으면 5
4로 이동하여 실행을 위해 마이크로태스크에서 작업을 꺼내고 돌아옵니다. 실행이 완료된 후 3단계로
5. 뷰 업데이트 실행
console.log('start') setTimeout(function() { console.log('setTimeout') }, 0) Promise.resolve().then(function() { console.log('promise1') }).then(function() { console.log('promise2') }) console.log('end')인쇄소에서 출력하는 로그 순서는 무엇인가요? 위 단계를 분석해 보면 참 쉽습니다~ 먼저 전역 코드(main())가 실행을 위해 호출 스택에 푸시되고 start가 인쇄됩니다. 그런 다음 setTimeout이 푸시됩니다. 매크로태스크 대기열과 promise.then 콜백이 배치됩니다. 마이크로태스크 대기열에 들어가서 마지막으로 console.log('end')를 실행하여 end를 출력합니다. 이 시점에서 호출 스택의 코드가 실행되었습니다. 매크로태스크의 정의로 돌아가서, 우리는 전역 코드가 매크로태스크에 속한다는 것을 알고 있습니다. 매크로태스크가 실행된 후 계속합니다. 다음 단계는 마이크로태스크 대기열의 작업을 실행하고, promise 콜백을 실행하고 promise1을 인쇄하는 것입니다.
promise回调函数默认返回undefined,promise状态变为fullfill触发接下来的then回调,继续压入microtask队列,event loop会把当前的microtask队列一直执行完,此时执行第二个promise.then回调打印出promise2;
这时microtask队列已经为空,从上面的流程图可以知道,接下来主线程会去做一些UI渲染工作(不一定会做),然后开始下一轮event loop,执行setTimeout的回调,打印出setTimeout;
这个过程会不断重复,也就是所谓的事件循环。
视图渲染的时机
回顾上面的事件循环示意图,update rendering(视图渲染)发生在本轮事件循环的microtask队列被执行完之后,也就是说执行任务的耗时会影响视图渲染的时机。通常浏览器以每秒60帧(60fps)的速率刷新页面,据说这个帧率最适合人眼交互,大概16.7ms渲染一帧,所以如果要让用户觉得顺畅,单个macrotask及它相关的所有microtask最好能在16.7ms内完成。
但也不是每轮事件循环都会执行视图更新,浏览器有自己的优化策略,例如把几次的视图更新累积到一起重绘,重绘之前会通知requestAnimationFrame执行回调函数,也就是说requestAnimationFrame回调的执行时机是在一次或多次事件循环的UI render阶段。
以下代码可以验证
setTimeout(function() {console.log('timer1')}, 0) requestAnimationFrame(function(){ console.log('requestAnimationFrame') }) setTimeout(function() {console.log('timer2')}, 0) new Promise(function executor(resolve) { console.log('promise 1') resolve() console.log('promise 2') }).then(function() { console.log('promise then') }) console.log('end')
可以看到,结果1中requestAnimationFrame()是在一次事件循环后执行,而在结果2,它的执行则是在三次事件循环结束后。
总结
1、事件循环是js实现异步的核心
2、每轮事件循环分为3个步骤:
a) 执行macrotask队列的一个任务
b) 执行完当前microtask队列的所有任务
c) UI render
3、浏览器只保证requestAnimationFrame的回调在重绘之前执行,没有确定的时间,何时重绘由浏览器决定
위 내용은 자바스크립트 브라우저의 이벤트 루프 메커니즘에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!