특정 코드가 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 실행하는 것
무언가를 비동기적으로 수행하는 함수는 함수 내 동작이 모두 처리된 후 실행되어야 하는 함수가 들어갈 콜백을 인수로 반드시 제공해야 한다.
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(script);
document.head.append(script);
}
loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => {
alert(`${script.src}가 로드되었습니다.`);
alert( _ ); // 스크립트에 정의된 함수
});
언뜻 봤을 때는 꽤 쓸만해보이지만, 꼬리에 꼬리를 무는 비동기 동작의 경우 문제가 될 수 있다.
loadScript('1.js', function(error, script) {
if (error) {
handleError(error);
} else {
// ...
loadScript('2.js', function(error, script) {
if (error) {
handleError(error);
} else {
// ...
loadScript('3.js', function(error, script) {
if (error) {
handleError(error);
} else {
// 모든 스크립트가 로딩된 후, 실행 흐름이 이어집니다. (*)
}
});
}
})
}
});위의 예시와 같이 계속된 호출로 인해 깊은 중첩 코드가 만들어지는 패턴을 콜백 지옥 혹은 멸망의 피라미드라고 부른다.
이를 해결하기위해 독립적 함수들로 분리할 수는 있지만, 코드의 진행 순서를 읽기가 불편해진다는 점과 재사용하지 않을 함수들을 각각 선언하면서 네임스페이스가 복잡해져버린다.
loadScript('1.js', step1);
function step1(error, script) {
if (error) {
handleError(error);
} else {
// ...
loadScript('2.js', step2);
}
}
function step2(error, script) {
if (error) {
handleError(error);
} else {
// ...
loadScript('3.js', step3);
}
}
function step3(error, script) {
if (error) {
handleError(error);
} else {
// 모든 스크립트가 로딩되면 다른 동작을 수행합니다. (*)
}
};자바스크립트의 비동기 작업을 처리하기 위한 객체
원하는 기능을 비동기적으로 실행하는 Promise
const promise = new Promise((resolve, reject) => {
console.log("promise.execute!");
});
프로미스 생성자를 통해 promise 객체를 생성하며, 매개변수로는 executor라는 callback 함수를 전달해줘야한다.
executor 함수는 프로미스가 생성되는 동시에 바로 실행된다.
executor 함수는 두 가지 매개변수를 갖는다.
producer가 제공한 데이터를 소비하는 것
then, catch, finally 를 이용하여 값을 받아올 수 있다.
then : 프로미스에서 가장 중요하고 기본이 되는 메서드다.
promise.then(
result => alert(result),
error => alert(error)
);
catch : 에러가 발생한 경우만 다루고 싶을 때 사용되는 메서드다.
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
// .catch(f)는 promise.then(null, f)과 동일하게 작동한다.
promise.catch(alert); //바로 아래 코드와 같은 의미
//promise.catch((error) => alert(error));
new Promise((resolve, reject) => {
/* 시간이 걸리는 어떤 일을 수행하고, 그 후 resolve, reject를 호출함 */
})
// 성공·실패 여부와 상관없이 프라미스가 처리되면 실행됨
.finally(() => 로딩 인디케이터 중지)
.then(result => result와 err 보여줌 => error 보여줌)배열 안의 프로미스가 모두 처리되면 새로운 프로미스가 이행되는데, 배열 안 프로미스 결과값을 담은 배열이 새로운 프로미스의 result가 된다.
let promise = Promise.all([...promises...]);
모든 프로미스가 처리될 때까지 기다렸다가 이행(거부) 여부와 상관없이 그 결과(객체)를 담은 배열을 반환한다.
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/Violet-Bora-Lee',
'https://no-such-url'
];
Promise.allSettled(urls.map(url => fetch(url)))
.then(results => { // (*)
results.forEach((result, num) => {
if (result.status == "fulfilled") {
alert(`${urls[num]}: ${result.value.status}`);
}
if (result.status == "rejected") {
alert(`${urls[num]}: ${result.reason}`);
}
});
});
결과
[
{status: 'fulfilled', value: ...응답...},
{status: 'fulfilled', value: ...응답...},
{status: 'rejected', reason: ...에러 객체...}
]
Promise.all과 비슷하지만, 가장 먼저 처리되는 프라미스의 결과를 반환한다는 점이 다르다.
let promise = Promise.race(iterable);
전달된 프라미스 중 하나가 먼저 이행(거부)된다면, 나머지 프로미스의 이행(거부) 여부의 상관 없이 첫번째 프로미스의 결과만 반환한다.
프로미스를 조금 더 편하게 사용할 수 있는 문법이다.
함수 앞에 async 키워드를 붙이면 해당 함수는 항상 프로미스를 반환한다.
async function f() {
return 1;
}
f().then(alert); // 1
async 함수 안에서만 동작한다.
자바스크립트는 await 키워드를 만나면 프라미스가 처리될 때까지 기다린다. 결과는 그 이후 반환된다.
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("완료!"), 1000)
});
let result = await promise; // 프라미스가 이행될 때까지 기다림 (*)
alert(result); // "완료!"
}
f();
awiat은 최상위 레벨 코드에서 작동하지 않는다.
프로미스가 정상적으로 이행되면 프라미스 객체의 result에 저장된 값을 반환한다.
반면 프로미스가 거부되면 마치 throw문처럼 에러가 던져진다.
async function f() {
try {
let response = await fetch('http://유효하지-않은-주소');
let user = await response.json();
} catch(err) {
// fetch와 response.json에서 발행한 에러 모두를 여기서 잡습니다.
alert(err);
}
}
f();
위와 같이 코드를 작성할 경우,
여러 줄의 코드에서도 오류를 감지 할 수 있다.