JavaScript는 언제 동기화됩니까?


202

JavaScript가 항상 비동기 적이라는 인상을 받았습니다. 그러나 나는 그렇지 않은 상황 (즉, DOM 조작)이 있다는 것을 알게되었습니다. 언제 동기화 될 것인지 언제 비동기 될 것인지에 대한 좋은 참조가 있습니까? jQuery가 이것에 전혀 영향을 줍니까?


14
항상 아약스를 제외하고.
defau1t

수락 된 답변이 잘못되어 잘못 인도되어 친절하게 확인하십시오.
Suraj Jain

2
이벤트 루프를 이해하기 위해 youtube.com/watch?v=8aGhZQkoFbQ 를 시청 하고 동기화 및 비동기와 관련하여 스택, 웹 API 및 작업 대기열이 작동하는 방식을 살펴 보는 것도 유용했습니다
mtpultz

1
@ defau1t 이것은 잘못된 것이 아닙니다. JavaScript는 항상 동 기적입니다. ajax 호출이 콜백을 마치면 큐에서 종료됩니다. Java 스크립트의 동 기적 특성에 대한 예외는 어떻습니까?
Suraj Jain

답변:


281

JavaScript는 항상 동기식이며 단일 스레드입니다. 페이지에서 JavaScript 코드 블록을 실행중인 경우 해당 페이지의 다른 JavaScript는 현재 실행되지 않습니다.

JavaScript는 Ajax 호출과 같이 비동기식 일뿐입니다. Ajax 호출은 실행을 중지하고 호출이 (성공적으로 또는 그렇지 않으면) 리턴 될 때까지 콜백이 동기식으로 실행될 때까지 다른 코드를 실행할 수 있습니다. 이 시점에서 다른 코드는 실행되지 않습니다. 현재 실행중인 다른 코드를 방해하지 않습니다.

JavaScript 타이머는 이와 동일한 종류의 콜백으로 작동합니다.

JavaScript를 비동기식으로 기술하는 것은 아마도 잘못된 것입니다. JavaScript가 다양한 콜백 메커니즘으로 동기식이며 단일 스레드라고 말하는 것이 더 정확합니다.

jQuery에는 Ajax 호출에 옵션이있는 동기식으로 만들기위한 async: false옵션이 있습니다. 초보자는 더 전통적인 프로그래밍 모델을 사용할 수 있기 때문에 이것을 잘못 사용하려고 할 수 있습니다. 문제가되는 이유는이 옵션이 모든 이벤트 핸들러 및 타이머를 포함하여 완료 될 때까지 페이지의 모든 JavaScript를 차단 하기 때문입니다.


31
죄송합니다.이 문장을 이해하지 못했습니다. "호출이 성공적으로 또는 오류가 발생할 때까지 코드 실행이 중지됩니다." 정교하게 할 수 있습니까? "실행중인 다른 코드를 방해하지 않습니다"라고 말할 때이 문장이 어떻게 사실 일 수 있습니까? 첫 번째 문장에서만 콜백 코드에 대해 이야기하고 있습니까? 제발 깨달아 줘
krishna

2
Nettuts에는 비동기의 기본 사항을 설명하는 데 매우 유용한 자습서가 있습니다. net.tutsplus.com/tutorials/javascript-ajax/…
RobW

26
@cletus 실행이 중지되지 않기 때문에 "호출이 반환 될 때까지 코드 실행이 중지됩니다"문을 수정해야합니다. 코드 실행을 계속할 수 있습니다. 그렇지 않으면 호출이 동기적임을 의미합니다.
HS.

1
나는 그 진술도 이해하지 못했다.
15:29에

12
이 답변은 엄청나게 오도되고 혼동됩니다. 대신 CMS 또는 Faraz Ahmad의 답변을 참조하십시오.
iono

214

JavaScript는 단일 스레드이며 동기식 실행 모델이 있습니다. 단일 스레드는 한 번에 하나의 명령이 실행되고 있음을 의미합니다. 동기는 한 번에 하나씩, 즉 코드가 나타나는 순서대로 한 줄의 코드가 실행되고 있음을 의미합니다. 따라서 JavaScript에서는 한 번에 한 가지 일이 발생합니다.

실행 컨텍스트

JavaScript 엔진은 브라우저에서 다른 엔진과 상호 작용합니다. JavaScript 실행 스택에는 맨 아래에 전역 컨텍스트가 있으며 함수를 호출하면 JavaScript 엔진이 각 함수에 대해 새 실행 컨텍스트를 작성합니다. 호출 된 함수가 종료되면 실행 컨텍스트가 스택에서 팝되고 다음 실행 컨텍스트가 팝됩니다.

예를 들어

function abc()
{
   console.log('abc');
}


function xyz()
{
   abc()
   console.log('xyz');
}
var one = 1;
xyz();

위의 코드에서 전역 실행 컨텍스트가 생성되고이 컨텍스트에서 var one 저장되며 그 값은 1이됩니다. xyz () 호출이 호출되면 새 실행 컨텍스트가 생성되며 변수를 정의한 경우 xyz 함수에서 이러한 변수는 xyz ()의 실행 컨텍스트에 저장됩니다. xyz 함수에서 abc ()를 호출 한 다음 abc () 실행 컨텍스트를 작성하고 실행 스택에 넣습니다. 이제 abc ()가 완료되면 컨텍스트가 스택에서 팝되고 xyz () 컨텍스트가 팝됩니다. 스택 및 전역 컨텍스트가 나타납니다 ...

이제 비동기 콜백에 대해; 비동기는 한 번에 둘 이상을 의미합니다.

실행 스택과 마찬가지로 이벤트 큐가 있습니다. 있습니다. JavaScript 엔진의 일부 이벤트에 대한 알림을 받으려면 해당 이벤트를 수신 할 수 있으며 해당 이벤트가 큐에 배치됩니다. 예를 들어 Ajax 요청 이벤트 또는 HTTP 요청 이벤트입니다.

위의 코드 예제와 같이 실행 스택이 비어있을 때마다 JavaScript 엔진은 주기적으로 이벤트 큐를보고 통지 할 이벤트가 있는지 확인합니다. 예를 들어, 큐에는 두 개의 이벤트 인 ajax 요청과 HTTP 요청이있었습니다. 또한 해당 이벤트 트리거에서 실행해야하는 함수가 있는지 확인합니다. 따라서 JavaScript 엔진은 이벤트에 대해 알림을 받고 해당 이벤트에서 실행할 각 함수를 알고 있습니다. 따라서 JavaScript 엔진은 예를 들어 처리기 함수 (예 : AjaxHandler ()가 호출되고 함수가 호출 될 때 항상 실행 컨텍스트가 실행 컨텍스트에 배치되고 이제 함수 실행이 완료되고 이벤트 ajax 요청도 이벤트 큐에서 제거됨) ... AjaxHandler ()가 완료되면 실행 스택이 비어 있으므로 엔진은 다시 이벤트 큐를보고 큐에서 다음에 있던 HTTP 요청의 이벤트 핸들러 함수를 실행합니다. 이벤트 큐는 실행 스택이 비어있는 경우에만 처리됩니다.

예를 들어 Javascript 엔진에 의한 실행 스택 및 이벤트 큐 처리를 설명하는 아래 코드를 참조하십시오.

function waitfunction() {
    var a = 5000 + new Date().getTime();
    while (new Date() < a){}
    console.log('waitfunction() context will be popped after this line');
}

function clickHandler() {
    console.log('click event handler...');   
}

document.addEventListener('click', clickHandler);


waitfunction(); //a new context for this function is created and placed on the execution stack
console.log('global context will be popped after this line');

<html>
    <head>

    </head>
    <body>

        <script src="program.js"></script>
    </body>
</html>

이제 웹 페이지를 실행하고 페이지를 클릭하고 콘솔의 출력을 확인하십시오. 출력은

waitfunction() context will be popped after this line
global context will be emptied after this line
click event handler...

JavaScript 엔진은 실행 컨텍스트 부분에서 설명한대로 코드를 동기식으로 실행하고 있으며 브라우저는 비동기식으로 이벤트 큐에 넣습니다. 따라서 완료하는 데 시간이 오래 걸리는 기능은 이벤트 처리를 방해 할 수 있습니다. 이벤트와 같은 브라우저에서 발생하는 상황은 JavaScript로 이러한 방식으로 처리됩니다. 리스너를 실행해야하는 경우 실행 스택이 비어있을 때 엔진이이를 실행합니다. 그리고 이벤트는 발생 순서대로 처리되므로 비동기 부분은 엔진 외부에서 발생하는 일, 즉 외부 이벤트가 발생할 때 엔진이 수행해야하는 작업에 관한 것입니다.

따라서 JavaScript는 항상 동 기적입니다.


16
이 답변은 매우 분명합니다. 더 많은 투표를해야합니다.
ranu

7
필자가 읽은 Javascript의 비동기 동작에 대한 가장 좋은 설명은 확실합니다.
Charles Jaimet

1
실행 컨텍스트 및 큐에 대한 좋은 설명.
Divyanshu Maithani

1
물론 이것은 실행 컨텍스트 스택에 대해 조금 읽어야하며 비어 있고 이벤트 큐만 추가하면 Java 스크립트 실행을 결정적으로 이해하는 것처럼 느껴집니다. 더 나쁜 것은 독서 한 페이지 만 필요하다고 느끼지만 어디에서나 찾기가 어렵다는 것입니다. 왜 아무도 그렇게 말하지 않습니까? 그들은 모르거나 무엇입니까? 그러나 js 튜토리얼 에이 기능이 있으면 시간을 많이 절약 할 수 있다고 생각합니다. > : |
마샬 크래프트

2
완벽한 설명!
Julsy

100

JavaScript는 단일 스레드이며 일반적인 동기 코드 흐름 실행을 위해 항상 작업합니다.

JavaScript가 가질 수있는 비동기 동작의 좋은 예는 이벤트 (사용자 상호 작용, Ajax 요청 결과 등) 및 타이머이며 기본적으로 언제든지 발생할 수있는 조치입니다.

다음 기사를 살펴 보는 것이 좋습니다.

이 기사는 JavaScript의 단일 스레드 특성과 타이머가 내부적으로 작동하는 방식 및 비동기 JavaScript 실행이 어떻게 작동하는지 이해하는 데 도움이됩니다.

비동기


허용되는 답변 오해의 소지가있는 경우 우리는 무언가를 할 수 있습니까? /
Suraj Jain

8

JS가 어떻게 작동하는지 실제로 이해하는 사람에게는이 질문이 보이지 않을 수도 있지만 JS를 사용하는 대부분의 사람들은 그렇게 깊은 통찰력을 가지고 있지 않으며 (필요하지는 않습니다) 그들에게 이것은 상당히 혼란스러운 요점입니다. 그런 관점에서 대답하려고 노력하십시오.

JS는 코드가 실행되는 방식에 동 기적입니다. 각 줄은 완료되기 전에 줄 뒤에서 만 실행되며 그 줄이 완료된 후에 함수를 호출하면 ...

혼란의 주된 이유는 브라우저가 JS에게 언제든지 더 많은 코드를 표시하도록 지시 할 수 있다는 사실에서 발생합니다 (콘솔에서 페이지에서 더 많은 JS 코드를 표시하는 방법과 유사). 예를 들어 JS에 콜백 함수가 있습니다.이 목적은 JS가 비동기 적으로 동작하도록 허용하여 실행 된 JS 함수 (예 : GET호출)가 응답을 반환하기 를 기다리는 동안 JS의 추가 부분을 실행할 수 있도록하는 것입니다. 브라우저는 그 시점에서 응답을 가지며 이벤트 루프 (브라우저)는 콜백 함수를 호출하는 JS 코드를 실행합니다.

이벤트 루프 (브라우저)는 JS가 비동기 적 인 의미에서 언제든지 더 많은 JS를 입력 할 수 있기 때문에 (브라우저가 JS 코드를 입력하게하는 주요 원인은 타임 아웃, 콜백 및 이벤트입니다)

나는 이것이 누군가에게 도움이 될만큼 명확하기를 바랍니다.


4

정의

"비동기"라는 용어는 약간 다른 의미로 사용될 수 있으며, 실제로는 그렇지 않지만 여기에서는 상충되는 답변으로 나타납니다. Asynchrony의 Wikipedia 에는 다음과 같은 정의가 있습니다.

컴퓨터 프로그래밍에서 비동기는 주 프로그램 흐름과 무관 한 이벤트 발생 및 이러한 이벤트를 처리하는 방법을 나타냅니다. 이는 신호 도착과 같은 "외부"이벤트이거나 프로그램 실행과 동시에 발생하는 프로그램에 의해 유발 된 동작으로 프로그램이 결과를 기다리지 않고 차단할 수 있습니다.

비 JavaScript 코드는 이러한 "외부"이벤트를 일부 JavaScript 이벤트 큐에 대기시킬 수 있습니다. 그러나 그것은 먼 길입니다.

선점권 없음

스크립트에서 다른 JavaScript 코드를 실행하기 위해 JavaScript 코드 실행에 대한 외부 중단 은 없습니다 . JavaScript 조각은 차례대로 실행되며 순서는 각 이벤트 큐의 이벤트 순서 및 해당 큐의 우선 순위에 따라 결정됩니다.

예를 들어, 다음 코드가 실행되는 동안 다른 스크립트 (동일한 스크립트 내)가 실행되지 않도록 절대 확신 할 수 있습니다.

let a = [1, 4, 15, 7, 2];
let sum = 0;
for (let i = 0; i < a.length; i++) {
    sum += a[i];
}

즉, JavaScript 에는 선점 이 없습니다 . 이벤트 큐에있는 것이 무엇이든 해당 이벤트 처리는 이러한 코드 조각이 완료 될 때까지 기다려야합니다. EcmaScript 사양은 8.4 작업 및 작업 대기열 섹션에 나와 있습니다 .

실행중인 실행 컨텍스트가없고 실행 컨텍스트 스택이 비어있는 경우에만 작업 실행을 시작할 수 있습니다.

비동기 성의 예

다른 사람들이 이미 작성했듯이 JavaScript에서 비동기가 발생하는 상황은 여러 가지가 있으며 항상 이벤트 큐가 포함되어 다른 JavaScript 코드가 실행되지 않는 경우에만 JavaScript가 실행될 수 있습니다.

  • setTimeout(): 시간 초과가 만료되면 에이전트 (예 : 브라우저)가 이벤트를 이벤트 큐에 넣습니다. 시간 모니터링 및 큐에서의 이벤트 배치는 비 JavaScript 코드에 의해 발생하므로 일부 JavaScript 코드의 잠재적 실행과 동시에 발생한다고 상상할 수 있습니다. 그러나 제공된 콜백 setTimeout은 현재 실행중인 JavaScript 코드가 완료되고 적절한 이벤트 큐를 읽을 때만 실행할 수 있습니다.

  • fetch(): 에이전트는 OS 기능을 사용하여 HTTP 요청을 수행하고 들어오는 응답을 모니터링합니다. 이 JavaScript 이외의 작업은 여전히 ​​실행중인 일부 JavaScript 코드와 병렬로 실행될 수 있습니다. 그러나에서 반환 한 약속을 해결하는 약속 해결 절차 fetch()는 현재 실행중인 JavaScript가 완료 될 때만 실행할 수 있습니다.

  • requestAnimationFrame(): 브라우저의 렌더링 엔진 (JavaScript가 아닌)은 페인트 작업을 수행 할 준비가되면 JavaScript 대기열에 이벤트를 배치합니다. JavaScript 이벤트가 처리되면 콜백 함수가 실행됩니다.

  • queueMicrotask(): 마이크로 태스크 대기열에 이벤트를 즉시 배치합니다. 콜백이 비어 있고 해당 이벤트가 소비되면 콜백이 실행됩니다.

더 많은 예제가 있지만 이러한 모든 기능은 핵심 EcmaScript가 아닌 호스트 환경에서 제공됩니다. 핵심 EcmaScript를 사용하면와 함께 Promise Job Queue에 이벤트를 동 기적으로 배치 할 수 있습니다 Promise.resolve().

언어 구성

ECMA 스크립트는 같은 비동기 패턴을 지원하기 위해 여러 언어 구조를 제공한다 yield, async, await. 그러나 실수는 없습니다 . 외부 이벤트 로 인해 JavaScript 코드가 중단 되지 않습니다 . 은 "방해" yieldawait제공 할 것이 (의 경우 JS 코드가 어느 함수 호출로부터 리턴하고 나중에 그 실행 컨텍스트를 복원 단지 제어, 미리 정의 된 방법 yield), 또는 경우의 이벤트 큐 ( await).

DOM 이벤트 처리

JavaScript 코드가 DOM API에 액세스 할 때 경우에 따라 DOM API가 하나 이상의 동기 알림을 트리거 할 수 있습니다. 그리고 코드에이를 수신하는 이벤트 핸들러가 있으면 호출됩니다.

이것은 선제 적 동시성으로 나타날 수 있지만, 그렇지 않습니다. 일단 이벤트 핸들러가 리턴되면 DOM API도 결국 리턴되고 원래 JavaScript 코드는 계속됩니다.

다른 경우 DOM API는 적절한 이벤트 큐에서 이벤트를 전달하고 호출 스택이 비워지면 JavaScript가이를 선택합니다.

참조 동기 및 비동기 이벤트를


0

모든 경우에 동 기적입니다.

다음을 사용하여 스레드를 차단하는 예 Promises:

  const test = () => new Promise((result, reject) => {
    const time = new Date().getTime() + (3 * 1000);

    console.info('Test start...');

    while (new Date().getTime() < time) {
      // Waiting...
    }

    console.info('Test finish...');
  });

  test()
    .then(() => console.info('Then'))
    .finally(() => console.info('Finally'));

  console.info('Finish!');

출력은 다음과 같습니다.

Test start...
Test finish...
Finish!
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.