
Promise에서 .then .catch가 왜 필요한지는 알꺼다. 하지만 다음을 보자.
function addPromise(x){ return new Promise(resolve => { doubleAfter2Seconds(10).then((a) => { doubleAfter2Seconds(20).then((b) => { doubleAfter2Seconds(30).then((c) => { resolve(x + a + b + c); }) }) }) }); } ```
Promise를 사용했을지라도, x+a+b+c 하는 코드일 뿐인데도, 또 하나의 콜백지옥에 들어가게 된다.
이를 해결하는 방법으로는
async,await이라는 keyword가 있다.
function doubleAfter2Seconds(x) { return new Promise(resolve => { setTimeout(() => { resolve(x * 2); }, 2000); }); } async function addAsync(x) { const a = await doubleAfter2Seconds(10); const b = await doubleAfter2Seconds(20); const c = await doubleAfter2Seconds(30); return x + a + b + c; } addAsync(10).then((sum) => { console.log(sum); });
async와 await 은 동기적 코드를 비동기적 코드처럼 만들 수 있는 keyword들이다. 아래 예제 2개는 정확히 같다고 볼 수 있다.
function getPersonsInfo(name) { return server.getPeople().then(people => { return people.find(person => { return person.name === name }); }); } async function getPersonsInfo(name) { const people = await server.getPeople(); const person = people.find(person => { return person.name === name }); return person; }
위의 예제의 server는 다음과 같다.
const server = { people: [ { name: "Odin", age: 20, }, { name: "Thor", age: 35, }, { name: "Freyja", age: 29, }, ], getPeople() { return new Promise((resolve, reject) => { // Simulating a delayed network call to the server setTimeout(() => { resolve(this.people); }, 2000); }); }, };
확인해보면 비동기적으로 서버의 데이터를 받은 후에 서버는 객체의 people의 name property를 확인해서 해당되는 조건에 맞으면 person을 뱉는 간단한 코드이다.
Promise를 간단하게 구현해놓은 것이라고 볼 수 있는데, 한번 확인해보자.
async 는 JS engine에 비동기적 함수를 선언하고 있다고 알려주는 것이다.
await 을 async 함수 안에 사용해야 한다. async 가 선언되면 항상 자동으로 Promise를 return한다. (return > resolve , throwing error > reject)
.then이 거의 필요하지 않고 .catch도 필요하지 않을 수 있지만 .then/catch를 추가해서 최종 결과나 처리되지 못한 에러를 다룬다.async function은 Promises 와 문법적으로 일치하다.async 는 function이 있는 모든 경우에 사용될 수 잇다.arrow functionconst yourAsyncFunction = async () => { // do something asynchronously and return a promise return result; };
anArray.forEach(async item => { // do something asynchronously for each item in 'anArray' // one could also use .map here to return an array of promises to use with 'Promise.all()' }); server.getPeople().then(async people => { people.forEach(person => { // do something asynchronously for each person }); });
await는 thenable 객체를 받는다. 이 객체는 프라미스가 아니지만 프라미스와 호환 가능한 객체라는 뜻이다. 밑의 예제에서는 class에서 then이라는 메서드가 존재해서 그걸 await가 thenable하다고 판단하여 비동기적으로 만든 것이다.
class Thenable { constructor(num) { this.num = num; } then(resolve, reject) { alert(resolve); // 1000밀리초 후에 이행됨(result는 this.num*2) setTimeout(() => resolve(this.num * 2), 1000); // (*) } }; async function f() { // 1초 후, 변수 result는 2가 됨 let result = await new Thenable(1); alert(result); } f();
메서드 앞에 async를 추가하면 async 클래스 메서드가 선언된다.
class Waiter { async wait() { return await Promise.resolve(1); } } new Waiter() .wait() .then(alert); // 1
함수를 진행하기 전에 asynchronous action을 기다리라고 JS에게 요청한다.
.then() 대신에 await keyword를 이용해서 function으로부터 value를 받을 수 있다.Promise.all
1. 여러 개의 프라미스가 처리되길 기다려야 하는 상황이면 Promise.all로 감싸고 여기에 await를 붙여서 사용할 수 있다.
// 프라미스 처리 결과가 담긴 배열을 기다립니다. let results = await Promise.all([ fetch(url1), fetch(url2), ... ]);
Error Handling
async도 .catch 를 이용해서 error handling을 할 수 있다.
async function f() {
let response = await fetch('http://유효하지-않은-주소');
}
// f()는 거부 상태의 프라미스가 됩니다.
f().catch(alert); // TypeError: failed to fetch // (*)
프라미스가 거부되기 전에 약간의 시간이 지체되는데 이런 경우 await가 에러를 던지기 전에 지연이 발생된다.
이러한 여러 error들을 try/catch 로 잡을 수 있다.
async function getPersonsInfo(name) {
try {
const people = await server.getPeople();
const person = people.find(person => { return person.name === name });
return person;
} catch (error) {
// Handle the error any way you'd like
}
}
1. async 키워드를 추가하면 두가지 효과
1. 함수는 프라미스를 반환
2. 함수 안에서만 await을 사용할 수 있다.
2. await을 붙이면 프라미스 처리까지 대기
1. 에러 발생 - 예외 발생
2. 에러 미발생 - 프라미스 객체의 result값을 반환