비동기 JavaScript

어떤 것들은 즉시 일어납니다: 두 수를 더하기, 변수 읽기, 페이지의 텍스트 변경하기. 다른 것들은 시간이 걸립니다. 타이머가 3초 동안 카운트다운됩니다. 서버에 요청이 네트워크를 통해 이동하고 잠시 후에 돌아옵니다. JavaScript가 각각을 기다리며 멈춘다면, 전체 페이지가 얼어붙을 것입니다: 클릭 없음, 스크롤 없음, 아무것도 없음, 느린 작업이 끝날 때까지. 하지만 그렇게 작동하지 않습니다. 이 장은 JavaScript가 어떻게 느린 작업을 시작하고, 페이지를 반응 가능하게 유지하고, 결과가 준비되었을 때 다시 돌아오는지에 관한 것입니다.
비동기가 필요한 이유
대부분의 JavaScript는 위에서 아래로 실행되며, 한 줄이 끝나야 다음 줄이 시작됩니다. 빠른 작업에는 괜찮습니다. 하지만 어떤 작업은 실제 시간이 걸리고, JavaScript는 그것을 기다리면서 앉아있지 않습니다. 느린 작업을 시작하고, 다음 줄로 이동하며, 완료되었을 때 다시 돌아옵니다. 이것이 **비동기**가 의미하는 바입니다: 순서대로 일어나지 않음, 지금 아님, 하지만 나중에, 결과가 준비되었을 때.
가장 간단한 예는 타이머입니다. setTimeout은 지연 후에 코드를 실행합니다:
console.log("시작");
setTimeout(() => {
console.log("3초 후");
}, 3000);
console.log("끝");
// 시작
// 끝
// 3초 후순서를 주목하세요. "끝"은 "3초 후"보다 먼저 출력되지만, 코드에서는 setTimeout 후에 나옵니다. JavaScript는 그 줄에서 3초를 기다리지 않았습니다. 타이머를 설정하고, 계속 진행했으며, 시간이 되었을 때 지연된 코드를 실행했습니다.
"끝"이 코드에서 타이머가 먼저 나타나도 setTimeout 메시지보다 먼저 출력되는 이유입니다. Promise
setTimeout은 지연에는 괜찮지만, 대부분의 비동기 작업은 당신이 신경 쓰는 값을 생성합니다: 요청한 데이터, 또는 문제가 있으면 오류. 이를 위해 JavaScript는 **Promise**를 사용합니다. Promise는 아직 준비되지 않은 값이며, 이미 예약된 공간을 가지고 있습니다. 음식을 받지 못했지만, 음식이 될 무언가를 가진 영수증 같은 것입니다.
Promise는 두 가지 방법 중 하나로 끝납니다. 작업이 성공하면 값으로 해결되거나, 실패하면 오류로 거부됩니다. .then()과 .catch()로 각 경우에 무엇을 할지 말합니다:
loadUser()
.then((user) => {
console.log(`${user.name}을(를) 로드했습니다`);
})
.catch((error) => {
console.log("뭔가 잘못되었습니다");
});.then()은 Promise가 해결될 때 실행되고, 값을 받습니다. .catch()는 거부되면 실행되고, 오류를 받습니다. 그 안의 코드는 나중에 결과가 도착했을 때 실행됩니다.
.then()으로, 오류에 대해 .catch()로 무엇을 할지 말하고, 둘 다 결과가 도착했을 때 나중에 실행됩니다. async / await
Promise 체인은 작동하지만, 보통 위에서 아래로 읽는 코드처럼 읽히는 더 깔끔한 방법이 있습니다. 두 개의 키워드, async와 **await**를 사용합니다.
함수를 async로 표시하고, 그 안에서 Promise를 await할 수 있습니다. await는 Promise가 해결될 때까지 함수를 멈추고, 그 다음 값을 직접 제공하며, .then()이 필요하지 않습니다:
async function showUser() {
const user = await loadUser();
console.log(`${user.name}을(를) 로드했습니다`);
}위에서 아래로 읽으세요: 사용자를 가져오고, 이름을 로그합니다. await 줄은 loadUser()가 완료될 때까지 기다리고 user를 제공합니다. 함수가 거기서 멈추지만, 페이지의 나머지는 계속 실행되므로, 아무것도 얼어붙지 않습니다.
오류를 처리하려면, await를 try와 catch로 감싸세요:
async function showUser() {
try {
const user = await loadUser();
console.log(`${user.name}을(를) 로드했습니다`);
} catch (error) {
console.log("사용자를 로드할 수 없습니다");
}
}Promise가 실패하면, 코드가 충돌하는 대신 catch 블록으로 점프합니다.
async로 표시하고 그 안에서 Promise를 await할 수 있습니다. await는 Promise가 해결될 때까지 함수를 멈추고 값을 직접 제공하므로, 코드가 .then() 없이 위에서 아래로 읽습니다. try와 catch로 감싸서 실패를 처리하고, 함수가 기다리는 동안 페이지는 계속 실행됩니다. 데이터 페칭
작성할 가장 일반적인 비동기 작업은 서버에서 데이터를 로드하는 것입니다. 브라우저는 이를 위해 fetch를 제공합니다. URL을 전달하면, 응답에 대한 Promise를 반환합니다:
async function loadUsers() {
const response = await fetch("https://api.example.com/users");
const users = await response.json();
console.log(users);
}두 개의 await가 있으며, 이것이 사람들을 처음에 놀라게 합니다. 첫 번째는 서버가 응답할 때까지 기다립니다. 두 번째는 그 응답의 본문을 읽고 .json()로 JavaScript 데이터로 변환하는데, 이것도 비동기입니다. 따라서 페칭은 두 단계입니다: 응답을 받고, 그것을 읽으세요.
서버는 "찾을 수 없음" 같은 오류로도 응답할 수 있습니다. fetch는 그것을 자체로 실패로 취급하지 않으므로, 당신은 response.ok를 스스로 확인합니다:
async function loadUsers() {
const response = await fetch("https://api.example.com/users");
if (!response.ok) {
console.log("요청이 실패했습니다");
return;
}
const users = await response.json();
console.log(users);
}fetch(url)은 서버의 응답에 대한 Promise를 반환하고, 데이터 로딩은 두 단계입니다: 응답을 위해 await fetch(...)하고, 그 다음 데이터를 읽기 위해 await response.json()합니다. "찾을 수 없음" 같은 서버 오류는 자체로 실패하지 않으므로, 본문을 읽기 전에 response.ok를 확인하세요. 두 단계 모두 시간이 걸리므로 둘 다 await를 사용합니다. 이벤트 루프와 병렬로 작업 실행
지금까지 모든 것은 하나의 테마를 가집니다: JavaScript가 느린 작업을 시작하고, 계속 이동하며, 나중에 그것으로 돌아옵니다. 이 섹션은 그 뒤의 메커니즘입니다. 더하기 몇 개의 비동기 작업을 잘 실행하기 위한 패턴입니다.
JavaScript가 비동기 작업을 추적하는 방법의 그림입니다. 실행 중인 코드는 호출 스택에 앉으며, 지금 실행 중인 것의 목록입니다. setTimeout을 호출하면, 타이머는 브라우저에 넘겨져 카운트다운하므로, 아무것도 블로킹하지 않는 스택에 있지 않습니다. 타이머가 완료되면, 그 콜백은 작업 큐로 이동하며, 실행할 준비가 된 코드의 대기선입니다.
**이벤트 루프**는 그들을 연결하는 부분입니다. 하나의 규칙을 가집니다: 먼저 호출 스택의 모든 것을 실행하고, 스택이 비워질 때만 큐에서 다음 작업을 가져옵니다. 이것이 setTimeout(fn, 0)이 지연이 0이어도 다른 코드 후에 실행되는 이유입니다. 콜백은 현재 코드가 완료될 때까지 큐에서 기다려야 합니다:
console.log("첫번째");
setTimeout(() => console.log("세번째"), 0);
console.log("두번째");
// 첫번째
// 두번째
// 세번째0의 지연은 "지금 아니라" "현재 코드가 완료될 때까지"를 의미합니다.
setTimeout(fn, 0)이 나머지 코드 후에 실행되는 이유입니다. 0의 지연은 "지금"이 아니라 "현재 코드가 완료될 때"를 의미합니다. 비동기가 여기서 어디로 가는지
비동기는 느린 작업이 백그라운드에서 일어나는 동안 페이지를 살려 두는 JavaScript의 부분입니다. 형태는 항상 동일합니다: 시간이 걸리는 무언가를 시작하고, 나머지 코드가 실행되도록 하며, 결과가 도착했을 때 처리합니다. Promise는 그 결과에 당신이 통과할 수 있는 값을 제공하고, async/await는 순수한 위에서 아래로 코드로 작성할 수 있도록 하며, fetch는 서버에서 데이터를 로드하면서 대부분 사용할 그곳입니다.
여기서부터, 비동기는 외부 세계에 반응하는 언어의 부분과 연결됩니다. 페칭된 데이터를 DOM을 통해 페이지로 연결하고, 당신은 페이지에서 사람들이 하는 것에 대응하여 비동기 작업을 시작할 것이며, 이것은 이벤트입니다. 당신이 되돌려 받는 결과는 거의 항상 객체이므로, 읽고 다시 형성하는 것이 여기의 모든 것과 쌍을 이루는 다음 기술입니다.

