JavaScript 비동기 이벤트 폴링에 대한 심층 분석

不言
풀어 주다: 2018-11-24 14:18:41
앞으로
2596명이 탐색했습니다.

이 글은 JavsScript 비동기 이벤트 폴링에 대한 심층적인 분석을 제공합니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

JavsScript는 단일 스레드 프로그래밍 언어입니다. 즉, 한 번에 하나의 작업만 처리할 수 있으며, 이는 JavaScript 엔진이 한 번에 하나의 스레드에서 하나의 명령문만 처리할 수 있음을 의미합니다.

단일 스레딩은 동시성으로 인해 발생하는 문제에 대해 너무 걱정할 필요가 없기 때문에 프로그래밍 코드를 단순화하지만, 메인 스레드를 차단하면서 네트워크 요청과 같은 장기 작업을 수행한다는 의미이기도 합니다.

API에서 일부 데이터를 요청하는 경우 상황에 따라 서버가 요청을 처리하는 데 시간이 걸리고 메인 스레드를 차단하여 웹페이지가 오랫동안 응답하지 않는 상태로 유지됩니다.

이것이 비동기 JavaScript가 도입된 이유입니다. 비동기 JavaScript(예: 콜백 함수, Promise, async/await)를 사용하면 메인 스레드를 차단하지 않고 오랫동안 네트워크 요청을 수행할 수 있습니다. :)

비동기 JavaScript 작동 방식을 알든 모르든 상관없습니다. 하지만 JavaScript 비동기 작동 방식을 더 깊이 이해하는 것이 도움이 됩니다.

자, 이제 시작하겠습니다 :)

동기 JavaScript는 어떻게 작동하나요?

엔진의 비동기 JavaScript之前,让我们首先了解同步 JavaScript 代码如何在 JavaScript 실행에 대해 자세히 알아보세요. 예:

    const second = () => {
      console.log('Hello there!');
    }
    
    const first = () => {
      console.log('Hi there!');
      second();
      console.log('The End');
    }
    
    first();
로그인 후 복사

위 코드가 JavaScript 엔진에서 어떻게 실행되는지 이해하려면 실행 컨텍스트와 호출 스택(실행 스택이라고도 함)의 개념을 이해해야 합니다.

함수 코드는 함수 실행 컨텍스트에서 실행되고, 전역 코드는 전역 실행 컨텍스트에서 실행됩니다. 각 함수에는 고유한 실행 컨텍스트가 있습니다.

Call Stack

Call Stack은 이름에서 알 수 있듯이 코드 실행 중에 생성된 모든 실행 컨텍스트를 저장하는 데 사용되는 LIFO(후입선출) 구조의 스택입니다.

JavaScript는 단일 스레드 프로그래밍 언어이기 때문에 호출 스택이 하나만 있습니다. 호출 스택에는 LIFO 구조가 있습니다. 즉, 스택 상단에서만 항목을 추가하거나 제거할 수 있습니다.

위의 코드 조각으로 돌아가서 JavaScript 엔진에서 코드가 어떻게 실행되는지 이해해 보겠습니다.

const second = () => {
  console.log('Hello there!');
}
const first = () => {
  console.log('Hi there!');
  second();
  console.log('The End');
}
first();
로그인 후 복사

JavaScript 비동기 이벤트 폴링에 대한 심층 분석

무슨 일이 일어나고 있는 걸까요?

이 코드가 실행되면 전역 실행 컨텍스트(main()으로 표시됨)가 생성되어 호출 스택의 맨 위로 푸시됩니다. first() 호출이 발생하면 스택의 맨 위로 푸시됩니다.

다음으로 console.log('Hi there!')가 스택의 맨 위로 푸시되고, 완료되면 스택에서 팝됩니다. 그런 다음 second()를 호출하므로 second() 함수가 스택의 맨 위로 푸시됩니다.

console.log('Hello there!')는 스택의 맨 위로 푸시되고 완료되면 스택에서 튀어나옵니다. second() 함수가 종료되므로 스택에서 제거됩니다.

console.log("the End")는 스택의 맨 위로 푸시되고 완료되면 삭제됩니다. 그 후 first() 함수가 완료되어 스택에서 제거됩니다.

이 시점에서 프로그램 실행이 완료되었으므로 전역 실행 컨텍스트(main())가 스택에서 제거됩니다.

비동기 JavaScript는 어떻게 작동하나요?

이제 호출 스택과 동기 JavaScript 작동 방식에 대한 기본적인 이해를 마쳤으니 비동기 JavaScript로 돌아가 보겠습니다.

블로킹이란 무엇인가요?

이미지 처리나 네트워크 요청을 동기식으로 수행한다고 가정해 보겠습니다. 예:

const processImage = (image) => {
  /**
  * doing some operations on image
  **/
  console.log('Image processed');
}
const networkRequest = (url) => {
  /**
  * requesting network resource
  **/
  return someData;
}
const greeting = () => {
  console.log('Hello World');
}
processImage(logo.jpg);
networkRequest('www.somerandomurl.com');
greeting();
로그인 후 복사

이미지 처리 및 네트워크 요청을 수행하는 데 시간이 걸리며, processImage() 함수가 호출되면 이미지 크기에 따라 시간이 걸립니다.

processImage() 함수가 완료되면 스택에서 제거됩니다. 그런 다음 networkRequest() 함수가 호출되어 스택에 푸시됩니다. 마찬가지로 실행을 완료하는 데도 시간이 걸립니다.

마지막으로 networkRequest() 함수가 완료되면 Greeting() 함수는 콘솔만 포함하기 때문에 호출됩니다. 로그 문 및 콘솔. 로그 문은 일반적으로 빠르므로 Greeting() 함수가 즉시 실행되고 반환됩니다.

따라서 processImage() 또는 networkRequest()와 같은 함수가 완료될 때까지 기다려야 합니다. 이는 이러한 함수가 호출 스택이나 기본 스레드를 차단한다는 의미입니다. 따라서 위 코드가 실행되는 동안에는 다른 작업을 수행할 수 없으며 이는 이상적이지 않습니다.

그래서 해결책은 무엇입니까?

가장 간단한 해결책은 비동기 콜백입니다. 비동기 콜백을 사용하여 코드를 차단하지 않도록 만듭니다. 예:

const networkRequest = () => {
  setTimeout(() => {
    console.log('Async Code');
  }, 2000);
};
console.log('Hello World');
networkRequest();
로그인 후 복사

여기에서는 setTimeout 메서드를 사용하여 네트워크 요청을 시뮬레이션했습니다. setTimeout은 JavaScript 엔진의 일부가 아니라 웹 API(브라우저 내) 및 C/C++ API(node.js 내)의 일부라는 점을 기억하세요.

이 코드가 어떻게 실행되는지 이해하려면 이벤트 폴링 및 콜백 대기열(또는 메시지 대기열)과 같은 더 많은 개념을 이해해야 합니다.

JavaScript 비동기 이벤트 폴링에 대한 심층 분석

事件轮询、web api和消息队列不是JavaScript引擎的一部分,而是浏览器的JavaScript运行时环境或Nodejs JavaScript运行时环境的一部分(对于Nodejs)。在Nodejs中,web api被c/c++ api所替代。

现在让我们回到上面的代码,看看它是如何异步执行的。

const networkRequest = () => {
  setTimeout(() => {
    console.log('Async Code');
  }, 2000);
};

console.log('Hello World');

networkRequest();

console.log('The End');
로그인 후 복사

JavaScript 비동기 이벤트 폴링에 대한 심층 분석

当上述代码在浏览器中加载时,console.log(' Hello World ') 被推送到堆栈中,并在完成后弹出堆栈。接下来,将遇到对 networkRequest() 的调用,因此将它推到堆栈的顶部。

下一个 setTimeout() 函数被调用,因此它被推到堆栈的顶部。setTimeout()有两个参数:

  • 1) 回调和

  • 2) 以毫秒(ms)为单位的时间。

setTimeout() 方法在web api环境中启动一个2s的计时器。此时,setTimeout()已经完成,并从堆栈中弹出。cosole.log(“the end”) 被推送到堆栈中,在完成后执行并从堆栈中删除。

同时,计时器已经过期,现在回调被推送到消息队列。但是回调不会立即执行,这就是事件轮询开始的地方。

事件轮询

事件轮询的工作是监听调用堆栈,并确定调用堆栈是否为空。如果调用堆栈是空的,它将检查消息队列,看看是否有任何挂起的回调等待执行。

在这种情况下,消息队列包含一个回调,此时调用堆栈为空。因此,事件轮询将回调推到堆栈的顶部。

然后是 console.log(“Async Code”) 被推送到堆栈顶部,执行并从堆栈中弹出。此时,回调已经完成,因此从堆栈中删除它,程序最终完成。

消息队列还包含来自DOM事件(如单击事件和键盘事件)的回调。例如:

document.querySelector('.btn').addEventListener('click',(event) => {
  console.log('Button Clicked');
});
로그인 후 복사

对于DOM事件,事件侦听器位于web api环境中,等待某个事件(在本例中单击event)发生,当该事件发生时,回调函数被放置在等待执行的消息队列中。

同样,事件轮询检查调用堆栈是否为空,并在调用堆栈为空并执行回调时将事件回调推送到堆栈。

延迟函数执行

我们还可以使用setTimeout来延迟函数的执行,直到堆栈清空为止。例如

const bar = () => {
  console.log('bar');
}
const baz = () => {
  console.log('baz');
}
const foo = () => {
  console.log('foo');
  setTimeout(bar, 0);
  baz();
}
foo();
로그인 후 복사

打印结果:

foo
baz
bar
로그인 후 복사

当这段代码运行时,第一个函数foo()被调用,在foo内部我们调用console.log('foo'),然后setTimeout()被调用,bar()作为回调函数和时0秒计时器。

现在,如果我们没有使用 setTimeout, bar() 函数将立即执行,但是使用 setTimeout 和0秒计时器,将bar的执行延迟到堆栈为空的时候。

0秒后,bar()回调被放入等待执行的消息队列中。但是它只会在堆栈完全空的时候执行,也就是在baz和foo函数完成之后。

ES6 任务队列

我们已经了解了异步回调和DOM事件是如何执行的,它们使用消息队列存储等待执行所有回调。

ES6引入了任务队列的概念,任务队列是 JavaScript 中的 promise 所使用的。消息队列和任务队列的区别在于,任务队列的优先级高于消息队列,这意味着任务队列中的promise 作业将在消息队列中的回调之前执行,例如:

const bar = () => {
  console.log('bar');
};

const baz = () => {
  console.log('baz');
};

const foo = () => {
  console.log('foo');
  setTimeout(bar, 0);
  new Promise((resolve, reject) => {
    resolve('Promise resolved');
  }).then(res => console.log(res))
    .catch(err => console.log(err));
  baz();
};

foo();
로그인 후 복사

打印结果:

foo
baz
Promised resolved
bar
로그인 후 복사

我们可以看到 promise 在 setTimeout 之前执行,因为 promise 响应存储在任务队列中,任务队列的优先级高于消息队列。

小结

因此,我们了解了异步 JavaScript 是如何工作的,以及调用堆栈、事件循环、消息队列和任务队列等概念,这些概念共同构成了 JavaScript 运行时环境。虽然成为一名出色的JavaScript开发人员并不需要学习所有这些概念,但是了解这些概念是有帮助的:)

위 내용은 JavaScript 비동기 이벤트 폴링에 대한 심층 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:segmentfault.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿