자바스크립트 비동기 처리

Doum Kim·2020년 4월 9일
2

Javascript

목록 보기
9/23
post-thumbnail

자바스크립트 비동기 방식

일단 비동기 방식을 알아보기 전에 동기, 비동기의 차이점을 먼저 살펴보자.
Synchronous (동기) 는 현금 인출기를 사용하는 상황과 비슷하다.
줄이 길게 서있는 현금 인출기에서 처리하고 있는 내용은 지금 현금 인출기를 사용하고 있는 고객의 계좌에 대한
처리일 것이다. 가장 앞의 고객의 은행 업무가 끝날 때까지 현금 인출기는 다른 고객에 대한 처리를 하지 못한다. 따라서 동기적 방식은 실행 순서가 확실하다.

반면에 Asynchronous (비동기) 는 음식점에서 주문을 받는 것과 비슷하다. 만약 당신이 음식을 주문하고 목이 말라 음식이 나오기 전에 음료를 달라고 종업원에게 요청했을 때 종업원은 보통 음식이 나오기 전에 음료를 가져다 줄 것이다. 완성까지 15분이 걸리는 음식의 완성 여부와는 상관 없이, 30초면 완료되는 음료를 먼저 가져다주는 즉, 주문한 순서보다 먼저 완성되는게 중요한 이러한 상황이 바로 비동기적 방식이다. 따라서 비동기적 방식은 실행 순서가 확실하지 않다.

그런데 자바스크립트는 싱글 스레드 언어이다. 그럼 과연 어떻게 음식을 만들면서 음료까지 만들 수 있을까?

Web API

자바스크립트는 위에서 말한것 처럼 싱글 스레드 언어이다. 즉, 같은 레벨의 함수들은 하나의 선로에 있는 열차처럼 순서대로 진행이 된다. (Call stack이 하나뿐이다.) 따라서 여러개의 이벤트를 처리할 때 뒤에 있는 열차들은 움직이지 못하고 앞의 열차들이 빠져나가기만을 기다려야한다. 따라서 자바스크립트는 이러한 task들을 Web API라는 다른 선로에 올려 놓는다.
Web API에서의 선로는 비동기 처리소라는 곳까지 각 열차에 개별적인 선로를 제공해준다.
따라서 우리가 음식을 만들면서 음료까지 만들 수 있는 것이다.

그리고 비동기 처리소에 도착한 순서대로 Task Queue 또는 Event Queue라는 하나의 선로에 순서대로 음식을 대기를 시킨다. 그리고 메인 선로(손님에게 가는 길)인 call stack이 모두 비워졌을 때 Event Loop라는 장치가 음식들을 순서대로 Call stack에 올려 놓는다. 즉, 가장 먼저 완성된 음식부터 순서대로 손님한테 나가는 것이다. 따라서 Web API로 들어오는 순서는 중요하지 않고, 어떤 task가 먼저 처리되느냐가 중요하다. 따라서 명확한 실행순서가 존재하지 않다.

하지만 서버에 A라는 데이터를 요청하는 비동기 처리 후에 그 A 데이터를 이용해 B정보를 다시 요청해야하는 상황이라면 첫 번째 A라는 데이터 요청, 두 번째 A를 이용해 B 정보를 요청 이라는 흐름이 중요해진다. A가 없는 상황에서 B를 불러오지를 못하기 때문이다. 따라서 이러한 흐름을 처리하는 방식으로 콜백함수가 있다.

콜백함수

자바스크립트의 함수는 일급 객체이며 다른 함수에 인수로 넘겨질 수 있다. 따라서 처리할 이벤트들을 순차적으로 콜백 함수로 넣어줄 수 있다.

콜백함수 사용 전

const posts = [
  { title: "Post One", body: "This is post one" },
  { title: "Post Two", body: "This is post two" }
];

const getPosts= () => {
    setTimeout(() => {
    let output = "";
    posts.forEach((post, index) => {
      output += `<li>${post.title}</li>`;
    });
    document.body.innerHTML = output;
  }, 1000);
 };

const creaetPost = (post) => {
  setTimeout(() => {
    posts.push(post);
  }, 2000);
};

createPost({ title: "Post Three", body: "This is post Three" });
getPosts();

비동기 작업의 흐름을 처리하지 못하면 이러한 문제점이 발생한다. createPost와 getPosts는 둘 다 비동기 방식이다. 따라서 Web API 선로를 따라서 비동기 처리소로 경주를 한다. 그런데 getPosts가 createPost보다 1초 빠르게 도착을 한다.

따라서 이벤트 큐에는 getPosts , createPost의 순서로 쌓이게 된다. 곧 이벤트 루프를 통해서 콜 스택에 같은 순서로 차례 차례 실행이 되므로 3번째 포스트를 만드는 createPost가 동작하기 전에 이미 getPosts가 동작을 해버려 위의 결과와 같이 2개의 포스트만 보여지게 된다.

따라서 이러한 흐름을 제어하기 위해서 콜백함수를 아래와 같이 사용해준다.

콜백함수 사용 후

const posts = [
  { title: "Post One", body: "This is post one" },
  { title: "Post Two", body: "This is post two" }
];

const getPosts = () => {
  setTimeout(() => {
    let output = "";
    posts.forEach((post, index) => {
      output += `<li>${post.title}</li>`;
    });
    document.body.innerHTML = output;
  }, 1000);
};

const createPost = (post, callback) => {
  setTimeout(() => {
    posts.push(post);
    callback(post);
  }, 2000);
};

createPost({ title: "Post Three", body: "This is post Three" }, getPosts);

하지만 이러한 콜백 함수들을 순차적으로 계속 사용하는 것은 가독성이 떨어지고 에러처리가 복잡해지는 일명 콜백 지옥이 발생한다. 이러한 콜백지옥에서 벗어나기 위해서 Promise라는 것을 이어서 알아보자.

0개의 댓글