728x90

본 강의는 자바스크립트의 기초를 대충 안다고 가정하고 시작하는 조금 심도 깊은 포스팅이다.

완전 처음부터 배우고 싶다면 다른 블로그나 책의 글을 참조하기를 바란다.

특별한 추가 설명이 없다면 nodejs가 아닌 브라우저에서 사용하는 js를 의미한다.


참고:

javascript docs

ecmascript specification


nodejs는 비동기로 작동하는 함수들이 많다. 심지어 동기코드를 비동기로 짜야하는 경우마져있다.(브라우저js에서는 매우 흔하다.)

setTimeout이나 setInterval등의 함수가 대표적인 비동기 루틴 생성함수이며 promise는 비동기루틴을 만드는 아주 효과적인 방법이다.

동기화에대한 promise 설명은 이 포스팅이 포스팅을 참고하라.


promise루틴은 콜백지옥을 해결해주는 아주 유용한 방법중 하나이다.

그러나 js를 쓰는 사람들은 이 콜백루틴에 대해서 아주 학을 떼는데 코드를 이해하는게 힘들기 때문이다.


function f(a) {
return new Promise((resolve, reject) => {
if (a > 10) {
resolve(a + 10);
} else {
reject(a - 10);
}
});
}

function g(b) {
return new Promise((resolve, reject) => {
if (typeof b === "number") {
resolve('b is number');
} else {
reject('b is not number');
}
});
}

function h(c) {
return new Promise((resolve, reject) => {
if (c.length <= 11) {
resolve('short');
} else {
reject('long');
}
});
}

가령 이런 코드가 존재한다고 보자.

3개의 함수 반환값은 promise이다.


기존에 이 함수를 f->g->h순으로 쓰고싶다면 아래 처럼 사용한다.


function main(num) {
f(num)
.then(g)
.then(h)
.then((value)=>{
console.log(value);
})
}

이러면 num은 f를 거치고 g를 거치고 h를 거치게된다.

만약 과정 중간중간에 액션을 취하고 싶다면 아래처럼 사용한다.


function main(num) {
f(num).then((a) => {
console.log(a);
return a;
}).then(g).then((b) => {
console.log(b);
return b;
}).then(h).then((c) => {
console.log(c);
return c;
}).then((value) => {
console.log(value)
})
}

프로미스 루틴에 익명함수를 선언해서 루틴을 추가해주면된다.

익명함수로 추가한 루틴들에는 반드시 return을 해줘야 다음 프로미스 루틴에서 그 값을 인자로 받아서 사용할 수 있다.


이게 정석적인 js사용법이지만 사실 이렇게 쓰기 싫어하는 사람들이 있다.

그래서 생긴게 바로 async await이다.


async function main(num) {
const a = await f(num);
const b = await g(a);
const c = await h(b);
console.log(c);
}

정말 간단하다. 몇가지 규칙만 알면 쉽게 사용할 수 있다.


async - 이 키워드의 대상이 되는 함수의 반환값은 promise가 된다. 즉 async키워드가 붙게되면 자동으로 프로미스 함수가된다.

await - 이 키워드의 대상이 되는 함수는 반환값을 낼때까지 기다린다. 대상이 되는 함수는 반드시 promise함수여야한다.


정말 말이 많은 키워드이다. 이 키워드의 등장으로 promise루틴을 아예 쓰지 않는 사람마저 존재한다.


async function f(a) {
if (a > 10) {
return a + 10;
} else {
throw a - 10;
}
}

async function g(b) {
if (typeof b === "number") {
return 'b is number';
} else {
throw 'b is not number';
}
}

async function h(c) {
if (c.length <= 11) {
return 'short';
} else {
throw 'long';
}
}

반환값은 promise루틴이다. return 값은 기존의 resolve에 해당된다.

throw는 기존의 reject에 해당한다. 이 키워드로 인해서 promise루틴은 더 쉬워진다.

기존의 코드에 써도 위화감 없이 작동한다.


promise와 async, await는 순수히 취향차이다.


원리는 promise나 async,await는 동일하다.

promise에서 가능한건 반대에서도 가능하고 반대에서 불가능한건 promise에서도 못한다.



async function main(num) {
const a = await f(num);
console.log(a);
const b = await g(a);
console.log(b);
const c = await h(b);
console.log(c);
const value = await ((d)=>d)(c);
console.log(value)
}

가령 이러한 코드가 있다고 치자.

promise루틴으로 번역하면 아래와 동일하다.


function main(num) {
f(num).then((a) => {
console.log(a);
return a;
}).then(g).then((b) => {
console.log(b);
return b;
}).then(h).then((c) => {
console.log(c);
return c;
}).then((value) => {
console.log(value)
});
}

promise에서 못하던거는 전체를 감싸는 try-catch문을 사용하지 못했다 가령 아래와 같은 상황이다.


async function main(num) {
try{
const a = await f(num);
const b = await g(a);
const c = await h(b);
console.log(c);
}catch (e) {
console.log(e)
}
}

이러한 코드를 짤수 있다는게 장점이다.

사실 이론상으로는 promise도 각각의 코드에 맞게 try-catch문을 일일히 삽입하는 수고를 해준다.

하지만 그게 이론상으로 하는것과 문법상으로 쉽게하는건 완전 다른 이야기이다.


하지만 이 두 키워드는 취향이 좀 갈린다.

코드가 간결해지고 sync한 코드를 짠다는 장점은 있다.

그런데 비동기 루틴임이 한눈에 보이지 않는다고 오히려 안좋아하는 사람들도 있다.


자신의 취향에 맞게 사용하면 될 것이다.

+ Recent posts