Asynchronous 즉, 비동기는 프로그램이 어떤 task를 실행할 때 동시에 다른 task를 실행할 수 있는 것이다. 실생활에서 많이 접할 수 있는 인터넷 로딩 창, 백그라운드 실행 등이 비동기 작업에 해당한다.
const printString = (string) => { setTimeout( () => { console.log(string) }, Math.floor(Math.random() * 100) + 1 ) } const printAll = () => { printString("A") printString("B") printString("C") } printAll()
이와 같이 비동기적으로 프로그램을 작성하면 콘솔에 찍히는 순서가 매번 다르게 나온다. 즉, 우리가 task의 순서를 제어할 수 없다. 만약 우리가 직접 순서를 제어하고 싶을 때에는 어떻게 해야할까? 이럴 때 callback을 사용한다.
callback을 배우기 전에 우리는 콜백함수가 실행되는 순서를 알아야한다.
- 비동기 요청과 함께 콜백함수를 전달한다.
- 비동기 요청이 처리된다.
- 전달한 콜백 함수가 실행된다.
이 사실을 기억하고 callback에 대해 알아보자.!!
1. callback
const printString = (string, callback) => { setTimeout( //일정 시간 뒤에 실행한다. () => { console.log(string) callback() //callback을 실행한다. }, Math.floor(Math.random() * 100) + 1 ) } const printAll = () => { printString("A", () => { //A가 실행된다. printString("B", () => { // A가 실행된 후 B를 실행한다. printString("C", () => {}) //A, B가 실행된 후 C를 실행한다. }) }) } printAll()
callback을 실행시킴으로써 작업이 동시에 실행되지 않고 순서대로 실행된다. 해당 코드를 실행시키면 콘솔에 A, B, C 순서대로 출력되는 것을 볼 수 있다.
그러나 A B C 3번만 실행시켜도 이렇게 코드가 길어지고, 가독성이 떨어지는데 10개 20개를 실행하려면 엄청나게 긴 코드가 필요할 것이다. 이러한 현상을 callback hell이라고 부른다.
그래서 조금 더 코드를 가독성있게 작성하기 위해 'promise'라는 개념이 등장하였다.
2. promise
const printString = (string) => { return new Promise((resolve, reject) => { setTimeout( //일정 시간 뒤에 실행한다. () => { console.log(string) resolve() }, Math.floor(Math.random() * 100) + 1 ) }) } const printAll = () => { printString("A") //A가 실행된다. .then(() => { return printString("B") // A가 실행된 후 B를 실행한다. }) .then(() => { return printString("C") //A, B가 실행된 후 C를 실행한다. }) } printAll()
[Promise의 파라미터]
-- resolve: 비동기 작업의 처리 과정에서 '성공' 했다는 것을 의미한다.
처리를 성공하였을 때 .then()을 이용하여 넘기는 인자를 사용할 수 있다.
-- reject: 비동기 작업의 처리 과정에서 '실패' 했다는 것을 의미한다.
처리를 실패하였을 때 .catch()를 이용하여 넘기는 인자를 사용할 수 있다.
[Promise의 세 가지 상태]
-- 대기(pending): 이행하거나 거부되지 않은 초기 상태를 의미한다.
-- 이행(fulfilled): 연산이 성공적으로 완료되었음을 의미한다.
-- 거부(rejected): 연산이 실패했음을 의미한다.
new 키워드를 이용해 Promise 객체를 만드는 순간 대기(pending)
상태가 된다. Promise 실행함수가 가지고 있는 두 개의 파라미터 중 resolve를 실행하면 이행(fulfilled)
상태가 된다. reject를 실행하면 거부(rejected)
상태가 된다.
promise를 사용하니 코드의 들여쓰기도 덜해지고, 일의 순서도 더 명확히 눈에 보이는 것을 볼 수 있다. 그러나 이것도 task가 많아지면 많아질수록 promise chaining이 발생할 수 있다.
promise chaining을 예방하고 코드를 더 깔끔하게 사용하기 위해 async await 라는 개념이 나왔다.
3. async await
const printString = (string) => { return new Promise((resolve, reject) => { setTimeout( //일정 시간 뒤에 실행한다. () => { console.log(string) resolve() }, Math.floor(Math.random() * 100) + 1 ) }) } const printAll = async () => { const a = await printString('A') const b = await pringString('B') const c = await printString('C') printAll(); }
await는 일반 함수에서 사용하면 에러가 발생한다. 반드시 async가 선언된 함수 내부에서 사용해야 한다. 이 점만 주의해서 사용하면 된다!
await 개념은 자바스크립트의 비동기 처리 패턴 중 가장 최근에 나온 문법이다.
기존의 비동기 처리 방식인 callback, promise의 단점을 보완하여 가독성이 좋은 코드를 작성할 수 있게 된다. 마치 일반 함수를 실행하는 것처럼 정갈하고 chaining도 더이상 일어나지 않는다는 장점이 있다
오늘은 동기와 비동기에 대하여 공부하였다.
내일은 웹에서 정보를 가져오는 방법인 fetch에 대하여 공부한다.
오늘은 여기까지 :)