WEB🔨/자바스크립트

[JavaScript] 비동기 처리

최문경 블로그 2021. 8. 16. 21:13

비동기 처리의 이해

자바스크립트는 동기적(Synchronous)으로 동작하는 언어이다. 따라서 순차적으로 코드가 실행된다.

동기라는 단어는 일한 차길이라고 생각하면 이해하기 쉽다. (from 얄팍한 코딩사전)

 

우리가 집안일을 할 때, 세탁기를 돌려 놓고 다른 집안일을 하는 것(비동기)이 더 빠르게 일을 처리할 수 있는 것처럼 프로그램이 실행될 때도 비동기(Asynchronous) 처리를 해야 하는 상황이 존재할 수 있다.

 

 

예를 들어, 다음과 같은 코드는 work()함수가 종료되고 나서야 console.log('다음 작업')이 실행된다.

따라서, 만약 work() 함수가 굉장히 오래걸리는 함수라면 그동안 다른 코드들이 실행되지 못하므로 사용자가 불편을 느끼는 상황이 발생할 수 있다.

이런 경우에 work() 함수 안에서 비동기 처리를 해준다면 work() 함수가 실행되는 동안에 다른 작업도 실행할 수 있다.

 

 

아래 코드와 같이 setTimeout을 사용하면 비동기 처리를 할 수 있다.

 

setTimeout에서 2번째 파라미터로 0을 넣어줬지만 실제로는 4ms 후에 코드를 실행시킨다고 한다.

 

 

아래와 같이 작업이 종료된 후 실행되는 함수인 callback함수를 사용하여 작성할 수도 있다.

 

 

 

 

하지만 callback 함수를 여러 번 사용해야 하는 경우 아래와 같이 callback 지옥에 빠질 수 있다.

 

 

 

Promise를 사용하면 콜백 지옥을 피할 수 있다.

function increaseAndPrint(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const increased = n + 1;
      console.log(increased);
      if (increased === 5) {
        const error = new Error();
        error.name = 'ValueIsFiveError';
        reject(error);
        console.log('작업 끝!');
      }
      resolve(increased);
    }, 1000);
  });
}

increaseAndPrint(0)
  .then((n) => increaseAndPrint(n))
  .then((n) => increaseAndPrint(n))
  .then((n) => increaseAndPrint(n))
  .then((n) => increaseAndPrint(n))
  .catch((e) => console.log(e));

 

 

아래와 같이 조금더 간결하게 작성할 수도 있다.

function increaseAndPrint(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const increased = n + 1;
      console.log(increased);
      if (increased === 5) {
        const error = new Error();
        error.name = 'ValueIsFiveError';
        reject(error);
        console.log('작업 끝!');
      }
      resolve(increased);
    }, 1000);
  });
}

increaseAndPrint(0)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .catch((e) => console.log(e));

 

 

아래와 같이 async, await을 사용하면 Promise를 더 쉽게 사용할 수 있다.

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function process() {
  console.log('작업 1');
  await sleep(1000);
  console.log('작업 2');
}

process();
console.log('다른 작업');

// 작업1
// 다른 작업
// 작업2

 

 

async 함수는 promise를 반환하기 때문에 아래와 같이 작성할 수도 있다.

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function process() {
  console.log('작업 1');
  await sleep(1000);
  console.log('작업 2');
  return '작업 끝';
}

process().then((v) => console.log(v));

 

 

throw error와 try catch를 사용하면 에러를 던지고 잡아낼 수 있다.

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function makeError() {
  await sleep(1000);
  const error = new Error();
  throw error;
}

async function process() {
  try {
    await makeError();
  } catch (e) {
    console.log(e);
  }
}

process();

 

 

아래와 같은 코드가 있을 때, 만약 여러개의 Promise를 동시에 처리하고 싶다면 Promise.all 또는 Promise.race를 사용하면 된다.

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

const getDog = async () => {
  await sleep(1000);
  return 'Dog';
};

const getRabbit = async () => {
  await sleep(500);
  return 'Rabbit';
};

const getTurtle = async () => {
  await sleep(2000);
  return 'Turtle';
};

async function process() {
  console.log(await getDog());
  console.log(await getRabbit());
  console.log(await getTurtle());
}

process();
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

const getDog = async () => {
  await sleep(1000);
  return 'Dog';
};

const getRabbit = async () => {
  await sleep(500);
  return 'Rabbit';
};

const getTurtle = async () => {
  await sleep(2000);
  return 'Turtle';
};

async function process() {
  const results = await Promise.all([getDog(), getRabbit(), getTurtle()]);
  console.log(results); // 3초 후 ["Dog", "Rabbit", "Turtle"]
}

process();

 

 

Promise.race는 가장 빠르게 반환하는 것을 알려준다.

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

const getDog = async () => {
  await sleep(1000);
  return 'Dog';
};

const getRabbit = async () => {
  await sleep(500);
  return 'Rabbit';
};

const getTurtle = async () => {
  await sleep(2000);
  return 'Turtle';
};

async function process() {
  const results = await Promise.race([getDog(), getRabbit(), getTurtle()]);
  console.log(results); // Rabit
}

process();

 

all은 하나라도 에러가 발생하면 에러를 잡아낼 수 있지만,

race는 가장 빨리 끝나는 것만 에러를 잡아낼 수 있다. 그 이후에 끝나는 것들은 에러를 잡아내지 못함.