asynchronous in JS

Asynchrounous in javascript

동기식? 비동기식?

자바스크립트는 싱글쓰레드 환경 내 동기식 처리(synchronous)(하나하나씩 작업 수행, 한 작업이 끝나기 전까지 다른작업 수행 안함) 을 기본으로한다. 콜스택 내에서 함수가 실행중인 경우 해당 함수 실행종료까지 다른 함수를 실행할 수 없는 (Blocking) 상태가 되는 것. 만약 모든 코드를 동기식으로만 작성 한다면 한 작업이 끝나기 전까지 다른 어떠한 작업도 할 수가 없고 대기해야되기 때문에 사용자 경험 측면에서 최악, 1~2초만 버벅거려도 해당 사이트를 꺼버리고 싶은 충동이 드는 요즘이라..

이러한 문제를 해결하기 위해 비동기식 처리 (Asynchronous)를 한다. 비동기식은 다른 작업이 끝날때까지 무한 대기가 아니라, 지정한 작업은 백그라운드에 처리를 위임하고, 동시에 다른 작업을 중단 없이 (Non-blocking) 작업을 수행하는 것이다. (javascript 에서는 이런류의 API들을 제공하여 비동기식 처리를 지원한다)

비동기식 처리 Process

( 자바스크립트 엔진은 코드를 한줄씩 순차적으로 수행함, 싱글쓰레드 환경 )

  1. 각 함수들은 execution stack에 추가, 삭제 되며 작업을 수행한다.
  2. 이 때 비동기식코드는 timer를 달고 background로 위임된다.
  3. 그 동안 다른 작업들은 막힘없이(Non-blocking) 순차적으로 실행 됨.
  4. 타이머가 끝난 call-back() 들은 message queue로 이동, 대기
  5. Event loop 는 execution stack 과 message que를 감시, execution stack 이 빌때마다 callback() 을 push 해서 실행시킨다.

동기식

1
2
3
4
5
6
7
8
9
10
11
12
13
const second() = () => {
console.log("second");
}
const first = () => {
console.log('first');
second();
console.log("finished!");
}
first();

// first
// second
// finished! 순으로 순차적으로 실행됨

비동기식

1
2
3
4
5
6
//image loading
const image = document.querySelector(".img").src;

processLargeImage(image, () => { // 오래걸리는 이미지 처리 callback()으로 비동기식 처리
//.....
})

비동기 처리 방법들

1. ES5 : Callback()

콜백함수(call-back)는 지금이 아닌 나중에 실행되는 함수다. 보통 함수의 인자 로 전달되어 특정 시점에 실행되도록 의도된 함수로서, 비동기적 처리가 요구되는 이벤트처리, 네트워크 응답, 파일입출입, 시간지연등에 전통적으로 사용되는 방식이다. 복잡한 작업수행을 위해서 비동기처리가 중첩되다보면은 소위 콜백지옥(Callback hell) 에 빠질 수 있고 에러발생 파악이 어려워 가독성 저하, 유지보수 어려움 등을 겪을 수 있다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getRecipe() {
setTimeout(() => {
const recipeID = [1,2,3,4];
console.log(recipeID);

setTimeout((id) => {
const recipe = {title: 'pizza', publisher: 'Min'};
console.log(`${id}: ${recipe.title}`);
setTimeout(publisher => {
const recipe2 = {title: 'pasta', publisher: 'Han'};
console.log(recipe);
//.... 이런식으로 계속 이전 비동기 처리 결과를 인자로 받아 Chaining 가능..
//.... 콜백함수가 너무 중첩되다보면 콜백지옥에 빠져 코드파악, 유지보수 어려워 지는 단점 존재..!
},1500, recipe.publisher);
}, 1000, recipeID[2]);
}, 2000);
}
getRecipe();

2. ES6: Promise

콜백의 단점을 해결하기 위해 ES6부터 공식 지원되는 방식이다. Promise의 핵심은 상태(State)를 분류해서 각 상태에 맞게 비동기 처리를 수행할 수 있다는 점이다.

1.이벤트 발생 전 (pending) 2.이벤트 발생 (settle / resolved)

3-1. 이벤트 성공 (Fulfilled)

3-2. 이벤트 실패 (Rejected)

  1. 이벤트 결과 처리 (Consume) - then() / catch()

이벤트 성공여부 상태에 따라 각각 핸들링 할 수 있기 때문에 간결한 코드 작성, 에러처리가 수월하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const getIDs =new Promise((resolve, reject) => { // 이벤트 성공시 resolve 호출, 실패시 reject
setTimeout(() => {
resolve([1,2,3,4]);
},1500);
});

const gerRecipe = recID => {
return new Promise((resolve, reject) => {
const recipe = {title: 'Pizza', publisher: 'John'};
resolve('${ID}: ${recipe.title}');
},1500, recID)
}

getIDs //Consume
.then(IDs => { // 이벤트 성공 시 then() 으로 결과처리
console.log(IDs); // [1,2,3,4];
return getRecipe(IDs[2]); // 이 함수는 또 promise 객체를 리턴 할 것이므로
})
.then(recipe => { // 리턴된 promise에 또 then()을 걸어 결과처리가능 (chaining!)
console.log(recipe);
});
.cathch(error => { // 이벤트 실패 시 catch() 으로 에러처리
console.log(error);
})

3. ES8: Async, Await

async,await 또한 promise 의 단점들을 보완하고자 탄생했다. promise 보다 훨씬 짧고 간결한 코드작성 가능, try/ catch 동일 방식으로 동기/비동기 에러 모두 처리가능, 디버깅 용이하다. 특히 백그라운드에서 비동기식으로 작동함에도 코드를 마치 동기식처럼 작성할 수 있어서 가독성 , 코드간결성 면에서는 탁월하다.

async 키워드 함수는 background 에서 실행되어 promise를 리턴하고, async 안의 await키워드 function은 그 promise 가 fulfilled 될때 까지 대기 했다가 promise를 consume (처리) 한다.

1
2
3
4
5
async function getRecipesAW() { // promise 를 리턴 하는 함수
const IDs = await getIDs; // await키워드는 promise 가 fulfilled 될때까지 코드실행x, 대기
const recipe = await getRecipe(IDs[2]);
}
getRecipesAW();

async 함수는 백그라운드에서 실행, 이벤트가 성공되기 전에까지 대기 하기 때문에

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function getRecipeAW() {
const IDs = await getIDs;
const recipe = await getRecipe(IDs[2]);
return recipe;
}

//아래 코드는 작동x, getRecipeAW()가 완료되기 전 console.log(rec)가 미리 실행되 버리기 때문
const rec = getRecipeAW();
console.log(rec); //Promise {<pending>}

//따라서 리턴되는 promise를 .then() 을 통해 완료 후 처리 하도록 해주면 정상작동 됨
getRecipeAw().then(recipe => {
console.log(recipe); // {title: 'Pizza, publisher: 'Min'};
})

4. fetch()

기존 XHR 객체 비해 좀 더 간결하게 코드작성 가능, 최신 API로 promise 객체를 결과로 리턴하고, Header, Request, Response 객체도 직접 핸들링 할 수 있는 특징이 있음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 무료 날씨 api metaweather.com/api 예제

//Promise + fetch
fetch// AJAX 를 처리해서 promise 객체 리턴
('https://cors-anywhere.herokuapp.com/https://www.metaweather.com/api/location/2487956/')
.then(result => { // default 방식: GET
return result.json(); //json객체로 변환
})
.then(data => { //다시 한번 .then() 으로 결과 가져오기
const today = data.consolidated_weather[0];
console.log(`${data.title} : ${today.min_temp} and ${today.max_temp} deg`)
})
.catch(error => {
console.log(error);
})
//San Francisco 12.5 and 24 deg

// 옵션으로 통신 방식 지정 가능
fetch("url",
method: "POST"})
.then(result => {
return result.json();
})
.then ( data => {
console.log(data.title);
})
.cathch(err => {
console.log(err);
}
1
2
3
4
5
6
7
8
// Async Await + fetch
async function getWeatherAW(woeid) {
const result = await fetch
('https://cors-anywhere.herokuapp.com/https://www.metaweather.com/api/location/2487956/');
const data = await result.json();
consloe.log(data);
}
getWeatherAW(44418);

https://jeong-pro.tistory.com/128

https://www.udemy.com/the-complete-javascript-course/

https://www.zerocho.com/category/HTML&DOM/post/595b4bc97cafe885540c0c1c

Share