본문 바로가기

프론트엔드/JavaScript

[JavaScript] 비동기

반응형

javascript 비동기

비동기

비동기 프로그래밍은 코드가 다른 코드와 동시에 실행될 수 있도록 하여, 시간이 오래 걸리는 작업(예: 네트워크 요청, 파일 읽기/쓰기 등)을 비동기적으로 처리할 수 있게 합니다.

 

콜백 함수 (a.k.a 콜백 지옥)

  • 순서를 보장하기 위해서 콜백 함수를 사용합니다.
function task1(callback) {
  console.log("task1");
  callback();
}

function task2(callback) {
  setTimeout(() => {
    console.log("task2");
    callback();
  }, 1000);
}

function task3(callback) {
  console.log("task3");
  callback();
}

function task4(callback) {
  console.log("task4");
  callback();
}

task1(() => {
  task2(() => {
    task3(() => {
      task4(() => {
        console.log("done");
      });
    });
  });
});

 

콜백 지옥 해결 방법: Promise ~ then

// 프로미스: 내부적으로 콜백을 반환
const promise = new Promise((resolve, reject) => {});
  • 프로미스 상태값: pending(대기), fulfill(이행), rejected(거부)

resolve

대기 → 이행 상태로 변경시켜야 합니다.

// 프로미스: 내부적으로 콜백을 반환
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve();
  }, 2000);
});

console.log(promise);

promise.then(() => {
  console.log("순서가 보장되어 이행.");
});

console.log(promise);

  • resolve가 호출되면 then()으로 넘겨받습니다.
  • resolve 함수 호출 시 값을 넘겨줄 수도 있습니다.
// 프로미스: 내부적으로 콜백을 반환
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reolve("Congraturations");
  }, 2000);
});

promise
  .then((message) => {
    console.log("순서가 보장되어 이행.", message);
  });

console.log(promise);

reject

// 프로미스: 내부적으로 콜백을 반환
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject();
  }, 2000);
});

console.log(promise);

promise
  .then(() => {
    console.log("순서가 보장되어 이행.");
  })
  .catch(() => {
    console.log("에러입니다.😰");
  });

console.log(promise);

  • reject() 사용 시 catch() 메서드로 에러 처리가 가능합니다.
  • reject() 함수 호출 시 인수로 값을 넘겨줄 수도 있습니다.

finally

promise
  .then((message) => {
    console.log("순서가 보장되어 이행.", message);
  })
  .catch(() => {
    console.log("에러입니다.😰");
  })
  .finally(() => {
    console.log("프로그램이 끝났습니다.");
  });
  • `finally`는 항상 실행되는 코드입니다.

 

콜백지옥 promise로 바꾸기

function task1() {
  return new Promise((resolve) => {
    console.log("task1");
    resolve();
  });
}

function task2() {
  return new Promise((resolve) => {
    console.log("task2");
    resolve();
  });
}

function task3() {
  return new Promise((resolve) => {
    console.log("task3");
    resolve();
  });
}

function task4() {
  return new Promise((resolve) => {
    console.log("task4");
    resolve();
  });
}

// task1().then(() => {
//   console.log("task1 done");
//   task2().then(() => {
//     console.log("task2 done");
//     task3().then(() => {
//       console.log("task3 done");
//       task4().then(() => {
//         console.log("task4 done");
//         console.log("done");
//       });
//     });
//   });
// });

task1()
  .then(() => task2())
  .then(() => task3())
  .then(() => task4())
  .then(() => console.log("done"));

 

 

콜백지옥 promise로 바꾸고 에러 처리

function task1() {
  return new Promise((resolve) => {
    console.log("task1");
    resolve();
  });
}

function task2() {
  return new Promise((resolve, reject) => {
    console.log("task2");
    reject("task2 error");
  });
}

function task3() {
  return new Promise((resolve) => {
    console.log("task3");
    resolve();
  });
}

function task4() {
  return new Promise((resolve) => {
    console.log("task4");
    resolve();
  });
}

task1()
  .then(() => task2())
  .then(() => task3())
  .then(() => task4())
  .then(() => console.log("done"))
  .catch((err) => {
    console.error(err);
  })

 

세부적으로 에러 처리

task1()
  .then(() => task2())
  .catch((err) => {
    console.error(err);
  })
  .then(() => task3())
  .then(() => task4())
  .then(() => console.log("done"))

  • 에러가 발생할 부분에 `catch`를 작성해주면 그 이후의 코드가 실행됩니다.

 

프로미스를 async, await로 바꾸기

// 프로미스
function getEmoji() {
  new Promise((resolve, reject) => {
    resolve("💕");
  });
}

getEmoji().then((emoji) => console.log(emoji));

// async, await
async function getEmoji() {
	//throw new Error("Error");
  return "💕";
}

async function takeEmoji() {
  const emoji= await getEmoji();
  console.log(emoji);
}

pickEmoji();

async await 예제

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

async function getApple() {
  await delay(2000);
  return "🍏";
}

async function getCherry() {
  await delay(4000);

  return "🍒";
}

async function pickFruits() {
  const apple = await getApple();
  const cherry = await getCherry();
  console.log(apple);
  console.log(cherry);
}

pickFruits();

  • pickFruits()를 호출하면 총 6초가 걸린 후에 결과가 출력됩니다.

병렬 처리로 데이터 받는 방법

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

async function getApple() {
  await delay(2000);
  return "🍏";
}

async function getCherry() {
  await delay(4000);
  return "🍒";
}

async function pickFruits() {
  const appleData = getApple();  // pending
  const cherryData = getCherry();  // pending

  const apple = await appleData;
  const cherry = await cherryData;

  console.log(apple, cherry);
}

pickFruits();

`await` 키워드 없이 getApple(), getCherry()가 동시 호출을 시작하고 이는 pending 되어 있습니다. 이후에 데이터가 올 때까지 `await` 해주면 사과 2초, 체리 4초로 총 4초만 걸립니다.

async function pickFruits() {
  const [apple, cherry] = await Promise.all([getApple(), getCherry()]);
  console.log(apple, cherry);
}

Promise.all() 을 이용해서 `async` 함수를 병렬 처리가 가능합니다. 그만큼 서버의 자원을 많이 사용하는 단점이 있습니다. 4초 후 결과가 출력됩니다. 하나라도 에러가 나면 전체 정지가 되는 단점이 있어서 Promise.allSettled()를 사용해서 성공한 거라도 값을 받을 수 있습니다.

반응형

'프론트엔드 > JavaScript' 카테고리의 다른 글

[JavaScript] 이벤트 리스너  (0) 2024.06.04
[JavaScript] 클래스  (0) 2024.06.03