>  기사  >  웹 프론트엔드  >  왜 비동기성이 있습니까? 이벤트 큐란 무엇입니까?

왜 비동기성이 있습니까? 이벤트 큐란 무엇입니까?

PHP中文网
PHP中文网원래의
2017-06-21 13:26:273216검색

소개

JavaScript를 공부해 본 사람이라면 이것이 단일 스레드 언어라는 것을 알고 있을 것입니다. 즉, JS는 멀티 스레드 프로그래밍을 수행할 수 없지만 JS에는 어디에나 존재하는 비동기 개념이 있다는 의미입니다. 초기에는 많은 사람들이 비동기를 멀티스레딩과 유사한 프로그래밍 모델로 이해했습니다. 실제로 비동기를 완전히 이해하려면 JS의 실행 핵심인 이벤트 루프를 이해해야 합니다. 이전에는 이벤트 루프에 대해 제한적으로만 이해했습니다. Philip Roberts의 연설을 보고 나서야 이벤트 루프에 대한 포괄적인 이해를 갖게 되었기 때문에 이에 대한 소개를 작성하고 싶었습니다. JS 이벤트 루프 문서는 모든 사람이 배우고 참조할 수 있는 것입니다.

1. 비동기성은 왜 존재하는가?

 JS에는 왜 비동기가 있나요? 코드를 동기적으로 실행하면 어떤 일이 일어날지 상상해 봅시다.

1 $.get(url, function(data) {2     //do something3 });

Ajax를 사용하여 통신할 때 기본적으로 비동기식으로 설정되어 있지만 동기식 실행으로 설정하면 어떻게 될까요? 작은 테스트 프로그램을 직접 작성하고 배경 코드를 5초 동안 지연시키면 ajax가 응답할 때까지 브라우저가 차단된 후 정상적으로 실행되는 것을 볼 수 있습니다. 이것은 비동기 모드가 해결해야 하는 주요 문제, 즉 브라우저가 작업을 비차단 방식으로 실행하도록 만드는 방법입니다. Ajax 요청을 동기식으로 실행하면 대기 시간이 알 수 없으며 네트워크 통신이 매우 빠르거나 느릴 수도 있고 이로 인해 브라우저가 차단될 수도 있습니다. 우리가 보고 싶지 않은 것입니다. 따라서 우리는 프로그램을 비동기적으로 처리할 수 있는 방법이 있기를 바랍니다. Ajax 요청이 언제 완료될지 신경 쓸 필요가 없으며, 응답한 후에 요청을 처리하는 방법만 알면 됩니다. 이 시간 동안 다른 작업을 수행할 수도 있습니다. 따라서 JavaScript 이벤트 루프입니다.

2. 이벤트 큐란 무엇인가요?

  먼저 간단한 코드를 살펴보겠습니다:

1 console.log("script start");2 3 setTimeout(function () {4     console.log("setTimeout");5 }, 1000);6 7 console.log("script end");

 여기에서 결과를 볼 수 있습니다:

  우선 프로그램이 'script start''script end를 출력하는 것을 볼 수 있습니다. ', 'setTimeout'이 약 1초 후에 출력되었습니다. 프로그램의 '스크립트 종료'는 1초가 출력될 때까지 기다리지 않고 즉시 출력합니다. 이는 setTimeout이 비동기 함수이기 때문입니다. 즉, 지연 기능을 설정하면 현재 스크립트가 차단되지 않고 브라우저의 이벤트 테이블에 기록되고 프로그램이 계속 실행됩니다. 지연 시간이 끝나면 이벤트 테이블은 이벤트 큐(작업 큐)에 콜백 함수를 추가합니다. 이벤트 큐가 작업을 가져온 후 해당 작업을 실행 스택(스택)에 푸시합니다. 실행 스택은 작업을 실행합니다 'setTimeout'.

  이벤트 큐는 실행할 작업을 저장하는 큐입니다. 작업은 엄격하게 시간순으로 실행되며 큐의 맨 앞에 있는 작업이 가장 나중에 실행됩니다. . 이벤트 큐는 한 번에 하나의 작업만 실행합니다. 작업이 완료된 후 다음 작업이 실행됩니다. 실행 스택은 함수 호출 스택과 유사한 실행 컨테이너입니다. 실행 스택이 비어 있으면 JS 엔진은 이벤트 큐가 비어 있지 않은지 확인하여 실행을 위해 첫 번째 작업을 실행 스택에 푸시합니다.

이제 위 코드를 약간 수정해 보겠습니다.

1 console.log("script start");2 3 setTimeout(function () {4     console.log("setTimeout");5 }, 0);6 7 console.log("script end");

지연 시간을 0으로 설정하고 프로그램이 어떤 순서로 출력되는지 살펴볼까요? 지연 시간을 아무리 설정해도 'setTimeout'은 항상 'script end' 이후에 출력됩니다. 일부 브라우저에는 최소 지연 시간이 있을 수 있으며 일부는 15ms, 일부는 10ms입니다. 이는 많은 책에서 언급되어 학생들에게 환상을 줄 수 있습니다. 프로그램이 매우 빠르게 실행되고 최소 지연 시간이 있으므로 'setTimeout' '스크립트 종료' 이후에 출력됩니다. 이제 환상을 없애기 위해 조금 바꿔보겠습니다.

 1 console.log("script start"); 2  3 setTimeout(function () { 4     console.log("setTimeout"); 5 }, 0); 6  7 //具体数字不定,这取决于你的硬件配置和浏览器 8 for(var i = 0; i < 999999999; i ++){ 9     //do something10 }11 12 console.log("script end");

  你可以在这里查看结果:


   可以看出,无论后面我们做了多少延迟性的工作,'setTimeout' 总是会在 'script end' 之后输出。所以究竟发生了什么?这是因为 setTimeout 的回调函数只是会被添加至事件队列,而不是立即执行。由于当前的任务没有执行结束,所以 setTimeout 任务不会执行,直到输出了 'script end' 之后,当前任务执行完毕,执行栈为空,这时事件队列才会把 setTimeout 回调函数压入执行栈执行。


  执行栈则像是函数的调用栈,是一个树状的栈:


 三、事件队列有何作用?

  通过以上的 demo 相信同学们都会对事件队列和执行栈有了一个基本的认识,那么事件队列有何作用?最简单易懂的一点就是之前我们所提到的异步问题。由于 JS 是单线程的,同步执行任务会造成浏览器的阻塞,所以我们将 JS 分成一个又一个的任务,通过不停的循环来执行事件队列中的任务。这就使得当我们挂起某一个任务的时候可以去做一些其他的事情,而不需要等待这个任务执行完毕。所以事件循环的运行机制大致分为以下步骤:

  1.   检查事件队列是否为空,如果为空,则继续检查;如不为空,则执行 2;

  2.   取出事件队列的首部,压入执行栈;

  3.        执行任务;

  4.        检查执行栈,如果执行栈为空,则跳回第 1 步;如不为空,则继续检查;

  然而目前为止我们讨论的仅仅是 JS 引擎如何执行 JS 代码,现在我们结合 Web APIs 来讨论事件循环在当中扮演的角色。

  在开始我们讨论过 ajax 技术的异步性和同步性,通过事件循环机制,我们则不需要等待 ajax 响应之后再进行工作。我们则是设置一个回调函数,将 ajax 请求挂起,然后继续执行后面的代码,至于请求何时响应,对我们的程序不会有影响,甚至它可能永远也不响应,也不会使浏览器阻塞。而当响应成功了以后,浏览器的事件表则会将回调函数添加至事件队列中等待执行。事件监听器的回调函数也是一个任务,当我们注册了一个事件监听器时,浏览器事件表会进行登记,当我们触发事件时,事件表便将回调函数添加至事件队列当中。


  我们知道 DOM 操作会触发浏览器对文档进行渲染,如修改排版规则,修改背景颜色等等,那么这类操作是如何在浏览器当中奏效的?至此我们已经知道了事件循环是如何执行的,事件循环器会不停的检查事件队列,如果不为空,则取出队首压入执行栈执行。当一个任务执行完毕之后,事件循环器又会继续不停的检查事件队列,不过在这间,浏览器会对页面进行渲染。这就保证了用户在浏览页面的时候不会出现页面阻塞的情况,这也使 JS 动画成为可能, jQuery 动画在底层均是使用 setTimeout 和 setInterval 来进行实现。想象一下如果我们同步的执行动画,那么我们不会看见任何渐变的效果,浏览器会在任务执行结束之后渲染窗口。反之我们使用异步的方法,浏览器会在每一个任务执行结束之后渲染窗口,这样我们就能看见动画的渐变效果了。

  考虑如下两种遍历方式:

 1 var arr = new Array(999); 2 arr.fill(1); 3 function asyncForEach(array, handler){ 4     var t = setInterval(function () { 5         if(array.length === 0){ 6             clearInterval(t); 7         }else { 8             handler(arr.shift()); 9         }10     }, 0);11 }12 13 //异步遍历14 asyncForEach(arr, function (value) {15     console.log(value);16 });17 18 //同步遍历19 arr.forEach(function (value, index, arr) {20     console.log(value);21 });

테스트 후 동기 순회 방법을 사용하면 배열 길이가 3자리로 증가하면 차단이 발생하지만 비동기 순회는 차단되지 않음을 알 수 있습니다(배열 길이가 매우 크지 않는 한 컴퓨터에 충분한 메모리 공간). 이는 동기 순회 방법이 별도의 작업이기 때문입니다. 이 작업은 다음 작업을 시작하기 전에 모든 배열 요소를 순회합니다. 비동기 순회 방법은 각 순회를 별도의 작업으로 분할합니다. 각 작업은 하나의 배열 요소만 순회하므로 각 작업 간에 브라우저가 렌더링할 수 있으므로 차단이 표시되지 않습니다. 다음 데모는 비동기 순회 전후에 어떤 일이 일어나는지 보여줍니다.


요약

이제 여러분은 JavaScript의 진정한 모습을 이해하셨다고 믿습니다. JavaScript는 단일 스레드 언어이지만 이벤트 루프 기능을 사용하면 프로그램을 비동기적으로 실행할 수 있습니다. 이러한 비동기 프로그램은 차례로 독립적인 작업입니다. 이러한 작업에는 setTimeout, setInterval, ajax, eventListener 등이 포함됩니다. 이벤트 루프와 관련하여 다음 사항을 기억해야 합니다.

  • 이벤트 큐는 엄격한 시간순으로 작업을 실행 스택에 푸시합니다.

  • 실행 스택이 비어 있으면 브라우저는 이벤트 큐를 계속 확인합니다. 비어 있지 않으면 첫 번째 작업을 수행합니다.

  • 각 작업이 끝나면 브라우저가 페이지를 렌더링합니다.

이 문서의 데모는 jsfiddle에 있습니다. 출처만 밝히세요. 이 기사에서 잘못된 점을 발견하면 댓글 영역에서 지적해 주시기 바랍니다.

위 내용은 왜 비동기성이 있습니까? 이벤트 큐란 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.