...
콜백 지옥을 탈출하는 새로운 문법
자바스크립트에서 '비동기 처리' 란 현재 실행중인 작업과는 별도로 다른 작업을 수행하는 것을 말한다. 예를 들어 서버에서 데이터를 받아오는 작업은 시간이 걸리기 때문에 자바스크립트의 서버 호출 함수는 비동기 함수(--링크--)로 이루어져 있다.
비동기는 특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 방식이기 때문에, 만일 비동기 작업의 결과에 따라 다른 작업을 수행해야 할 때는 전통적으로 콜백 함수를 사용했다. 콜백 함수란 비동기 작업이 완료되면 호출되는 함수의 의미로서, 비동기 함수의 매개변수로 함수 객체를 넘기는 기법을 말한다. 그래서 함수 내부에서 함수 호출을 통해 비동기 작업의 결과를 받아서 인자로 주면 이를 통해 후속 처리 작업을 수행할 수 있다. 하지만 콜백 함수를 사용하면 코드가 복잡하고 가독성이 떨어지는 문제가 있다. 특히, 여러 개의 비동기 작업을 순차적으로 수행해야 할 때는 콜백 함수가 중첩되어 코드의 깊이가 깊어지는 현상이 발생한다. 이러한 현상을 콜백 지옥(callback hell) 이라고 부른다.
콜백 함수의 비동기 처리 문법
숫자 n 을 파라미터로 받아와서 다섯번에 걸쳐 1초마다 1씩 더해서 출력하는 작업을 setTimeout 비동기 함수로 구현한 코드이다. 한눈에 봐도 콜백함수를 연달아 써서 코드의 깊이가 깊어졌다. 보기에 매우 안좋은 걸 볼 수 있다.
function increaseAndPrint(n, callback) {
setTimeout(() => {
const increased = n + 1;
console.log(increased);
if (callback) {
callback(increased); // 콜백함수 호출
}
}, 1000);
}
increaseAndPrint(0, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
console.log('끝!');
});
});
});
});
});
코드가 마치 '아됴겐' 을 맞은 것처럼 들여쓰기 코드의 깊이가 활 처럼 휘어져 있는 웃기는 모습이다.
이러한 콜백 함수의 코드 형태는 콜백 함수가 중첩되면서 들여쓰기 수준이 깊어져 코드의 가독성을 떨어뜨리며 코드의 흐름을 파악하기 어려워진다. 또한 콜백 함수마다 에러 처리를 따로 해줘야 하고, 에러가 발생한 위치를 추적하기 힘들게 된다.
프로미스로 개선된 비동기 처리 문법
이를 자바스크립트의 Promise 객체를 이용해 리팩토링하면, 비동기 작업의 개수가 많아져도 들여쓰기 코드의 깊이가 깊어지지 않게 된다. 아직 프로미스 문법에 대해 잘 모르지만 .then() 을 통해 이들을 열거하여 비동기 처리 결과를 깔끔하게 표현했음을 볼 수 있다.
function increaseAndPrint(n) {
return new Promise((resolve, reject)=>{
setTimeout(() => {
const increased = n + 1;
console.log(increased);
resolve(increased);
}, 1000)
})
}
increaseAndPrint(0)
.then((n) => increaseAndPrint(n))
.then((n) => increaseAndPrint(n))
.then((n) => increaseAndPrint(n))
.then((n) => increaseAndPrint(n)); // 체이닝 기법
이처럼 자바스크립트 프로미스는 비동기 프로그래밍의 근간이 되는 기법 중 하나이다. 프로미스를 사용하면 콜백 함수를 대체하고, 비동기 작업의 흐름을 쉽게 제어할 수 있다. 지금부터 이 자바스크립트의 Promise 사용법에 대해 알아보도록 하자.
자바스크립트 프로미스 객체
자바스크립트 Promise 객체는 비동기 작업의 최종 완료 또는 실패를 나타내는 Array나 Object 처럼 독자적인 객체라고 보면 된다. 비동기 작업이 끝날 때까지 결과를 기다리는 것이 아니라, 결과를 제공하겠다는 '약속'을 반환한다는 의미에서 Promise라 명명 지어졌다고 한다.
프로미스 객체 기본 사용법
프로미스 객체 생성
Promise 객체를 생성하려면 new 키워드와 Promise 생성자 함수를 사용하면 된다. 이때 Promise 생성자 안에 두개의 매개변수를 가진 콜백 함수를 넣게 되는데, 첫 번째 인수는 작업이 성공했을 때 성공(resolve)임을 알려주는 객체이며, 두 번째 인수는 작업이 실패했을 때 실패(reject)임을 알려주는 오류 객체이다.
Promise 생성자안에 들어가는 콜백 함수를 executor 라고 부른다.
const myPromise = new Promise((resolve, reject) => {
// 비동기 작업 수행
const data = fetch('서버로부터 요청할 URL');
if(data)
resolve(data); // 만일 요청이 성공하여 데이터가 있다면
else
reject("Error"); // 만일 요청이 실패하여 데이터가 없다면
})
위 코드에서 서버로부터 요청하는 비동기 작업이 성공하느냐 실패하느냐에 따라 매개변수를 호출하는 것이 나뉨을 볼 수 있다. 만일 작업이 성공한다면 비동기 로직 실행이 참이라는 걸 알려주기 위해 말 그대로 resolve() 성공 메서드를 호출한다. 실패하면 reject() 메서드를 호출한다. 마치 스위치를 내려 전기를 흐르게 하거나 올려서 안흐르게 하거나 같은 원리이다.
프로미스 객체 처리
이렇게 만들어진 Promise 객체는 비동기 작업이 완료된 이후에 다음 작업을 연결시켜 진행할 수 있다. 작업 결과 따라 .then() 과 .catch() 메서드 체이닝을 통해 성공과 실패에 대한 후속 처리를 진행할 수 있다.
만일 처리가 성공하여 프로미스 객체 내부에서 resolve(data) 를 호출하게 되면, 바로 .then() 으로 이어져 then 메서드의 콜백 함수에서 성공에 대한 추가 처리를 진행한다. 이때 호출한 resolve() 함수의 매개변수의 값이 then 메서드의 콜백 함수 인자로 들어가 then 메서드 내부에서 프로미스 객체 내부에서 다룬 값을 사용할 수 있게 된다.
반대로 처리가 실패하여 프로미스 객체 내부에서 reject("Error") 를 호출하게 되면, 바로 .catch() 로 이어져 catch 메서드의 콜백 함수에서 성공에 대한 추가 처리를 진행한다.
myPromise
.then((value) => { // 성공적으로 수행했을 때 실행될 코드
console.log("Data: ", value); // 위에서 return resolve(data)의 data값이 출력된다
})
.catch((error) => { // 실패했을 때 실행될 코드
console.error(error); // 위에서 return reject("Error")의 "Error"가 출력된다
})
.finally(() => { // 성공하든 실패하든 무조건 실행될 코드
})
프로미스 함수 등록
위와 같이 프로미스 객체를 변수에 바로 할당하는 방식을 사용할 수도 있지만, 보통은 다음과 같이 별도로 함수로 감싸서 사용하는 것이 일반적이다.
// 프로미스 객체를 반환하는 함수 생성
function myPromise() {
return new Promise((resolve, reject) => {
if (/* 성공 조건 */) {
resolve(/* 결과 값 */);
} else {
reject(/* 에러 값 */);
}
});
}
// 프로미스 객체를 반환하는 함수 사용
myPromise()
.then((result) => {
// 성공 시 실행할 콜백 함수
})
.catch((error) => {
// 실패 시 실행할 콜백 함수
});
함수를 만들고 그 함수를 호출하면 프로미스 생성자를 return 함으로서, 곧바로 생성된 프로미스 객체를 함수 반환값으로 얻어 사용하는 기법이다. 이렇게 프로미스 객체를 함수로 만드는 이유는 다음 3가지 정도가 있다.
- 재사용성 : 프로미스 객체를 함수로 만들면 필요할 때마다 호출하여 사용함으로써, 반복되는 비동기 작업을 효율적으로 처리할 수 있다.
- 가독성 : 프로미스 객체를 함수로 만들면 코드의 구조가 명확져, 비동기 작업의 정의와 사용을 분리하여 코드의 가독성을 높일 수 있다.
- 확장성 : 프로미스 객체를 함수로 만들면 인자를 전달하여 동적으로 비동기 작업을 수행할 수 있다. 또한 여러 개의 프로미스 객체를 반환하는 함수들을 연결하여 복잡한 비동기 로직을 구현할 수 있다.
프로미스를 생성하여 반환하는 함수를 '프로미스 팩토리 함수' 라고 불리우기도 한다.
이러한 점 때문에 실무에서 프로미스 객체를 사용할 일이 있다면 함수로 감싸 사용한다. 그래서 대부분의 자바스크립트 비동기 라이브러리도 함수 형태로 프로미스 객체를 제공한다. 대표적으로 자바스크립트의 fetch() 메서드가 있는데, 이 fetch() 메서드 내에서 프로미스 객체를 생성하여 서버로부터 데이터를 가져오면 resolve() 하여 .then() 으로 처리하기 때문이다.
// GET 요청 예시
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => response.json()) // 응답 객체에서 JSON 데이터를 추출한다.
.then((data) => console.log(data)); // JSON 데이터를 콘솔에 출력한다.
프로미스 3가지 상태
프로미스는 비동기 작업의 결과를 약속하는 것이다. new Promise() 생성자로 프로미스 객체를 생성하면, 그 비동기 작업은 이미 진행 중이고 언젠가는 성공하거나 실패할 것이다. 이러한 진행중, 성공, 실패 상태를 나타내는 것이 바로 프로미스의 상태(state)라고 불리운다. 쉽게 말하자면 일종의 프로미스 처리 과정이라고 보면 된다.
- Pending(대기) : 처리가 완료되지 않은 상태 (처리 진행중)
- Fulfilled(이행) : 성공적으로 처리가 완료된 상태
- Eejected(거부) : 처리가 실패로 끝난 상태
1. Pending 상태
대기(pending) 상태란, 말 그대로 아직 비동기 처리 로직이 완료 되지 않은 상태이다. 예를 들어 아래와 같이 프로미스 객체를 생성하고, 생성한 프로미스 객체를 콘솔을 찍어보면 아래와 같이 프로미스 객체의 상태가 "pending"으로 출력되게 된다.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("처리 완료")
}, 5000)
});
console.log(promise); // Pending (대기) 상태
2. Fulfilled 상태
위의 프로미스 코드를 실행하고 5초 동안 기다리다가 다시 콘솔을 찍어보자. 그럼 아래와 같이 이행(fulfilled) 상태로 변하게 된다.
즉, 5초가 지나 resolve() 수행되면서 프로미스 성공을 알리는 개체를 호출 하였으니 이행 상태로 변한 것이다. 따라서 이행(fulfilled) 상태란, 비동기 처리 로직이 성공적으로 완료 됬다는 것을 표현하기 위한 상태라고 보면 된다.
프로미스의 '이행' 상태를 다르게 표현하자면 '완료' 봐도 된다.
그리고 이행 상태로 변한 프로미스 객체는 바로 체이닝된 .then() 메서드를 호출하여 처리 결과 값을 받을 수 있는 것이다.
promise
.then((data) => {
console.log("프로미스가 이행 상태가 되면서 처리에 대한 결과를 수행")
})
3. Rejected 상태
resolve()를 호출함으로서 프로미스 객체 상태가 이행(fulfilled) 상태가 됬다면, 반대로 reject()를 호출하면 프로미스 객체가 실패(rejected) 상태가 된다.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject("처리 실패")
}, 5000)
});
역시 실패 상태로 변한 프로미스 객체는 바로 체이닝된 .catch() 메서드를 호출하여 처리 실패에 대한 행동을 수행하게 된다.
promise
.catch((error) => {
console.log(error);
console.log("실패에 대한 후속 조치...")
})
프로미스 핸들러
프로미스가 생성되면, 그 작업은 이미 진행 중이고 언젠가는 성공하거나 실패할 것이다. 그 성공/실패 결과를 .then / .catch / .finally 핸들러를 통해 받아 다음 후속 작업을 수행할 수 있다. 프로미스 핸들러는 프로미스의 상태에 따라 실행되는 콜백 함수라고 보면 된다.
.then(): 프로미스가 이행(fulfilled)되었을 때 실행할 콜백 함수를 등록하고, 새로운 프로미스를 반환.catch(): 프로미스가 거부(rejected)되었을 때 실행할 콜백 함수를 등록하고, 새로운 프로미스를 반환.finally(): 프로미스가 이행되거나 거부될 때 상관없이 실행할 콜백 함수를 등록하고, 새로운 프로미스를 반환
프로미스 핸들러 구성을 보건데, 마치 try - catch - finally 구조와 굉장히 유사하다고 느낄텐데 그 느낌이 맞다.
이미 위에서 프로미스 상태에 따라 수행되는 프로미스 핸들러 문법에 대해 다뤘으니 기본적인 사용법은 다들 알 것이다. 다만 이 섹션에서 소개하고 싶은 것은 이 핸들러들을 체이닝(chaining) 하여 열거하여 복잡하게 사용할때 처리 순서에 대한 부분이다.
프로미스 체이닝
프로미스 체이닝이란, 프로미스 핸들러를 연달아 연결하는 것을 말한다. 이렇게 하면 여러 개의 비동기 작업을 순차적으로 수행할 수 있다는 특징이 있다.
예를들어 아래는 doSomething 함수를 호출하여 프로미스를 생성하고, then 메서드를 통해 이행 핸들러를 연결하는 과정을 보여준다. 각 이행 핸들러는 이전 프로미스의 값에 50을 더한 값을 반환하고, 마지막 이행 핸들러는 최종 값을 콘솔에 출력하게 된다.
function doSomething() {
return new Promise((resolve, reject) => {
resolve(100)
});
}
doSomething()
.then((value1) => {
const data1 = value1 + 50;
return data1
})
.then((value2) => {
const data2 = value2 + 50;
return data2
})
.then((value3) => {
const data3 = value3 + 50;
return data3
})
.then((value4) => {
console.log(value4); // 250 출력
})
이런식으로 체이닝이 가능한 이유는 then 핸들러에서 값을 리턴하면, 그 반환값은 자동으로 프로미스 객체로 감싸져 반환되기 때문이다. 그리고 다음 then 핸들러에서 반환된 프로미스 객체를 받아 처리하는 것이다. 그래서 프로미스 상태의 흐름도를 표현하자면 아래와 같이 된다.
만일 연결된 이행 핸들러에서 중간에 오류가 있는 처리를 행한다면 예외처리를 함으로써 catch 핸들러에 점프하도록 설정할 수 있다. 어렵게 생각하지 말고 try - catch 와 같이 생각하면 된다.
function doSomething(arg) {
return new Promise((resolve, reject) => {
resolve(arg)
});
}
doSomething('100A')
.then((value1) => {
const data1 = value1 + 50; // 숫자에 문자를 연산
if (isNaN(data1))
throw new Error('값이 넘버가 아닙니다')
return data1
})
.then((value2) => {
const data2 = value2 + 50;
return data2
})
.catch((err) => {
console.error(err);
})
만일 catch 핸들러 다음으로 then 핸들러가 이어서 체이닝 되어 있다면, 에러가 처리되고 가까운 then 핸들러로 제어 흐름이 넘어가 실행이 이어지게 된다.
new Promise((resolve, reject) => {
throw new Error("에러 발생!");
})
.catch(function(error) {
console.log("에러가 잘 처리되었습니다. 정상적으로 실행이 이어집니다.");
})
.then(() => {
console.log("다음 핸들러가 실행됩니다.")
})
.then(() => {
console.log("다음 핸들러가 또 실행됩니다.")
});
프로미스 정적 메서드
프로미스 객체는 생성자 함수 외에도 여러 가지 정적 메서드(static method)를 제공한다. 정적 메서드는 객체를 초기화 & 생성하지 않고도 바로 사용할 수 있기 때문에 비동기 처리를 보다 효율적이고 간편하게 구현할 수 있도록 도와준다.
Promise.resolve()
보통 프로미스의 resolve() 메서드는, 프로미스를 생성자로 만들고 그안의 콜백 함수의 매개변수를 통해 호출하여 사용해왔다.
// 프로미스 생성자를 사용하여 프로미스 객체를 반환하는 함수
function getPromiseNumber() {
return new Promise((resolve, reject) => {
const num = Math.floor(Math.random() * 10); // 0 ~ 9 사이의 정수
resolve(num); // 프로미스 이행
});
}
이러한 과정을 Promise.resolve() 정적 메서드로 한번에 호출할 수 있도록 편의 기능을 제공해 주는 것으로 보면 된다. 그래서 프로미스 정적 메서드를 이용하면 프로미스 객체와 전혀 연관없는 함수 내에서 필요에 따라 프로미스 객체를 반환하여 핸들러를 이용할 수 있도록 응용이 가능하다. 이 방법은 비동기 작업을 수행하지 않는 함수에서도 프로미스의 장점을 활용하고 싶은 경우에 유용하다.
// 프로미스 객체와 전혀 연관없는 함수
function getRandomNumber() {
const num = Math.floor(Math.random() * 10); // 0 ~ 9 사이의 정수
return num;
}
// Promise.resolve() 를 사용하여 프로미스 객체를 반환하는 함수
function getPromiseNumber() {
const num = getRandomNumber(); // 일반 값
return Promise.resolve(num); // 프로미스 객체
}
// 핸들러를 이용하여 프로미스 객체의 값을 처리하는 함수
getPromiseNumber()
.then((value) => {
console.log(`랜덤 숫자: ${value}`);
})
.catch((error) => {
console.error(error);
});
Promise.reject()
마찬가지로 주어진 사유로 거부하는 Promise 객체를 반환한다.
// 주어진 사유로 거부되는 프로미스 생성
const p = Promise.reject(new Error('error'));
// 거부 사유를 출력
p.catch(error => console.error(error)); // Error: error
Promise.all()
배열, Map, Set에 포함된 여러개의 프로미스 요소들을 한꺼번에 비동기 작업을 처리해야 할때 굉장히 유용한 프로미스 정적 메소드이다. 모든 프로미스 비동기 처리가 이행(fulfilled) 될때까지 기다려서, 모든 프로미스가 완료되면 그때 then 핸들러가 실행하는 형태로 보면 된다. 가장 대표적인 사용 예시로 여러 개의 API 요청을 보내고 모든 응답을 받아야 하는 경우에 사용할 수 있다.
// 1. 서버 요청 API 프로미스 객체 생성 (fetch)
const api_1 = fetch("https://jsonplaceholder.typicode.com/users");
const api_2 = fetch("https://jsonplaceholder.typicode.com/users");
const api_3 = fetch("https://jsonplaceholder.typicode.com/users");
// 2. 프로미스 객체들을 묶어 배열로 구성
const promises = [api_1, api_2, api_3];
// 3. Promise.all() 메서드 인자로 프로미스 배열을 넣어, 모든 프로미스가 이행될 때까지 기다리고, 결과값을 출력
Promise.all(promises)
.then((results) => {
// results는 이행된 프로미스들의 값들을 담은 배열.
// results의 순서는 promises의 순서와 일치.
console.log(results); // [users1, users2, users3]
})
.catch((error) => {
// 어느 하나라도 프로미스가 거부되면 오류를 출력
console.error(error);
});
Promise.allSettled()
Promise.all() 메서드의 업그레이드 버전으로, 주어진 모든 프로미스가 처리되면 모든 프로미스 각각의 상태와 값 (또는 거부 사유)을 모아놓은 배열을 반환한다.
// 1초 후에 1을 반환하는 프로미스
const p1 = new Promise(resolve => setTimeout(() => resolve(1), 1000));
// 2초 후에 에러를 발생시키는 프로미스
const p2 = new Promise((resolve, reject) => setTimeout(() => reject(new Error('error')), 2000));
// 3초 후에 3을 반환하는 프로미스
const p3 = new Promise(resolve => setTimeout(() => resolve(3), 3000));
// 세 개의 프로미스의 상태와 값 또는 사유를 출력
Promise.allSettled([p1, p2, p3])
.then(result => console.log(result));
Promise.any()
Promise.all() 메서드의 반대 버전으로, Promise.all() 이 주어진 모든 프로미스가 모두 완료해야만 결과를 도출한다면, Promise.any() 는 주어진 모든 프로미스 중 하나라도 완료되면 바로 반환하는 정적 메서드이다.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("promise1 failed");
}, 3000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("promise2 succeeded");
}, 2000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("promise3 failed");
}, 1000);
});
// promise1, promise2, promise3은 각각 3초, 2초, 1초 후에 거부되거나 이행
Promise.any([promise1, promise2, promise3])
.then((value) => {
console.log(value); // "promise2 succeeded"
})
.catch((error) => {
console.error(error);
});
위 코드를 보면 Promise.any() 메서드의 결과로 promise2의 처리가 가장 먼저 도출됨을 볼 수 있다. 오로지 첫번째로 이행(fulfilled) 된 프로미스만을 취급하기 때문에 나머지 promise1과 promise3의 거부(rejected)는 무시되게 된다.
만일 요청된 모든 프로미스가 거부(rejected)되면, AggregateError 객체를 사유로 하는 거부 프로미스를 반환한다.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("promise1 failed");
}, 3000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("promise2 succeeded");
}, 2000);
});
const promise4 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("promise4 failed");
}, 4000);
});
Promise.any([promise1, promise3, promise4])
.then((value) => {
console.log(value);
})
.catch((error) => {
console.error(error); // AggregateError: All promises were rejected
console.error(error.errors); // ["promise3 failed", "promise1 failed", "promise4 failed"]
});
Promise.race()
Promise.race() 는 Promise.any() 와 같이 여러 개의 프로미스 중 가장 먼저 처리된 프로미스의 결과값을 반환하지만, 차이점이 존재한다.
Promise.any()는 가장 먼저 fulfilled(이행) 상태가 된 프로미스만을 반환하거나, 혹은 전부 rejected(실패) 상태가된 프로미스(AggregateError)를 반환한다. 반면 Promise.race() 는 fulfilled(이행), rejected(실패) 여부 상관없이 무조건 처리가 끝난 프로미스 결과값을 반환하다.
마치 프로미스 참가자들이 레이싱(race) 경주를 하는것을 떠올리면 된다.
아래 예시 코드를 보면 좀 더 이해가 수월할 것이다.
콜백 지옥을 이은 프로미스 지옥
자바스크립트 프로미스 객체를 처음 소개할때 콜백 지옥을 극복하는 신세대의 문법이라고 자신있게 소개하였으면서 콜백 지옥을 이은 프로미스 지옥(Promise Hell)이라니 헛 웃음만 나올 것이다. 그런데 콜백 못지않게 프로미스의 then() 메서드가 지나치게 체인되어 반복되면 코드가 장황해지고 가독성이 굉장히 떨어질 수 가 있다.
아래 예시 코드는 fetch 함수를 사용하여 깃허브 API에서 유저 정보를 가져오고, then 메서드를 여러 번 연결하여 유저들의 로그인 이름을 쉼표로 구분한 문자열로 만들어 출력하는 비동기 작업을 수행한다. 동작 자체는 문제가 없을 테지만, 이런식으로 then을 늘어뜨어 놓으면 코드가 길어지고, 각 then 메서드가 어떤 값을 반환하는지 파악하기 어렵게 된다. 또한, catch 메서드가 마지막에 한 번만 사용되어 있기 때문에, 중간에 발생할 수 있는 에러나 예외 상황에 대응하기 어렵다.
fetch("https://api.github.com/users")
.then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error("Network Error");
}
})
.then((users) => {
return users.map((user) => user.login);
})
.then((logins) => {
return logins.join(", ");
})
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
});
이를 또다시 극복하기 위해 나온 자바스크립트의 신세대 문법이 있는데 바로 async/await 키워드이다. async/await 키워드는 ES8에서 도입된 비동기 처리를 위한 문법으로, 프로미스를 기반으로 하지만 then과 catch 메서드를 사용하지 않고 비동기 작업을 수행할 수 있다. async/await 키워드를 사용하면 비동기 작업을 마치 동기 작업처럼 쓸 수 있어서 코드가 간결하고 가독성이 좋아지게 된다.
try {
const response = await fetch("");
if (response.ok) {
const users = await response.json();
const logins = users.map((user) => user.login);
const result = logins.join(", ");
console.log(result);
} else {
throw new Error("Network Error");
}
} catch (error) {
console.error(error);
}
정신 없는 프로미스 핸들러를 없애버리고 1 라인으로 비동기 코드들을 구성했음을 볼 수 있다. async/await 문법에 대해서 자세한 사용법은 아래 포스팅을 참고하길 바란다.
# 참고자료
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://ko.javascript.info/promise-basics
https://velog.io/@ljinsk3/JavaScript-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-Promise-%EA%B0%9D%EC%B2%B4
이 글이 좋으셨다면 구독 & 좋아요
여러분의 구독과 좋아요는
저자에게 큰 힘이 됩니다.