비동기

Dongwoo Joo·2023년 3월 20일
0

codestates bootcamp

목록 보기
17/48

동기(synchronous)
동기 처리: 특정 코드 실행이 완료된 후 다음 코드 실행.
동시에 작업하지 못함.

비동기(asynchronous)
비동기 처리: 특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드 실행

JavaScript의 작동 원리
싱글 스레드 기반으로 동작하는 언어이다. 즉, 동기적으로 작동한다.
하지만, Javascript가 동작하는 환경(런타임)에서
비동기 처리를 도와준다.

타이머 관련 API
setTimeout(callback, millisecond)
일정 시간 후에 함수를 실행
매개변수(parameter): 실행할 콜백 함수, 콜백 함수 실행 전 기다려야 할 시간 (밀리초)
return 값: 임의의 타이머 ID

clearTimeout(timerId)
setTimeout 타이머를 종료
매개변수(parameter): 타이머 ID
return 값: 없음

setInterval(callback, millisecond)
일정 시간의 간격을 가지고 함수를 반복적으로 실행
매개변수(parameter): 실행할 콜백 함수, 반복적으로 함수를 실행시키기 위한 시간 간격 (밀리초)
return 값: 임의의 타이머 ID

clearInterval(timerId)
setInterval 타이머를 종료
매개변수: 타이머 ID
return 값: 없음

비동기 코드는 코드가 작성된 순서로 작동되는 것이 아니라,
동작이 완료되는 순서대로 작동한다.
=> 코드의 순서를 예측할 수 없다.

하지만, 개발자는 프로그램을 제어해야 하기 때문에,
예측 가능한 코드를 작성해야 한다.
즉, 비동기로 작동하는 코드를 제어할 수 있는 방법을 알아야 한다.

비동기로 작동하는 코드 제어 방법 중 하나는,
callback 함수 활용이다.
비동기 코드의 순서를 제어한다. (비동기를 동기화)

// 터미널에 `node index.js`를 입력하여 비동기 코드가 작동하는 순서를 확인해보세요.
const printString = (string, callback) => {
  setTimeout(function () {
    console.log(string);
    callback();
  }, Math.floor(Math.random() * 100) + 1);
};
// printString 은 문자열을 출력하는 함수이다.
// setTimeout 함수를 사용해, 주어진 랜덤시간(1~100ms) 후에 콘솔에 문자열을 출력한다.
// 이후 콜백 함수를 호출한다.

const printAll = () => {
  printString('A', () => {
    printString('B', () => {
      printString('C', () => {});
    });
  });
};

printAll();

// printAll 함수를 호출하여, 'A'를 출력하고, 콜백 함수를 전달한다.
// 이 콜백 함수에서는 'B'를 출력하고, 또 다른 콜백 함수를 전달한다.
// 이 콜백 함수에서는 'C'를 출력하지만, 더 이상의 콜백 함수를 전달하지 않는다.

// 마지막으로 'printAll' 함수를 호출하고, 그 다음 콘솔에 메시지를 출력한다.

console.log(
  `아래와 같이 Callback 함수를 통해 비동기 코드의 순서를 제어할 수 있습니다!`
);

이 코드에서 동기화된 부분은 콜백 함수를 사용하여 'printString' 함수를 연속적으로 호출한 부분이다.
이러한 방식으로 콜백 함수를 사용해서 비동기 코드를 순차적으로 실행시킬 수 있다.

단점: Callback Hell
콜백 함수로 비동기 코드의 순서를 제어할 수 있지만,
코드가 길어질 수록 복잡해져 가독성이 떨어진다. 이를 callback hell이라 한다.
이를 방지하기 위해 "Promise" 가 사용된다.

Promise: 비동기 코드 제어 방법

Promise 는 클래스이다.
-> new 키워드를 통해 Promise 객체를 생성한다.

Promise 는 비동기 처리를 수행할 콜백 함수(executor)를 인수로 전달받는데,
이 콜백 함수는 resolve, reject 함수를 인수로 전달받는다.

Promise 객체가 생성되면, executor는 자동으로 실행되고 작성했던 코드들이 작동된다.
코드가 정상처리 되었다면 resolve 함수를 호출하면 되고,
에러가 발생한 경우는 reject 함수를 호출하면 된다.

let promise = new Promise((resolve, reject) => {
    resolve(value); // 정상적으로 처리되는 경우
                    // resolve의 인자에 값을 전달할 수도 있다.
    
    reject(error);  // 에러가 발생하는 경우
                    // reject의 인자에 에러 메시지를 전달할 수도 있다.

Promise 객체의 내부 프로퍼티
new Promise 가 반환하는 Promise 객체는
내부 프로퍼티로 state, result 를 갖는다.
하지만, 직접 접근할 수 없고,
.then, .catch, .finally 의 메서드를 사용해야 접근 가능하다.

State
기본 상태는 pending(대기)이다.
비동기 처리를 수행할 콜백함수(executor)가 성공적으로 작동했다면,
fulfilled(이행)로 변경이 되고,
에러 발생 했다면,
rejected(거부) 된다.
// 유어 클래스 예제의 사진을 보면,
// promise 정상 처리 경우 -> PromiseState: "fulfilled"
// promise 에러 발생 경우 -> PromiseState: "rejected"
로 출력된 것이 보인다.

Result
처음은 undefined 이다.
비동기 처리를 수행할 콜백 함수가
성공적으로 작동하여, resolve(value)가 호출되면, value
에러가 발생하여, reject(error)가 호출되면, error로 변한다.

.then, .catch, .finally

Then
executor에 작성한 코드가 정상 처리 되면,
resolve 함수를 호출하고,
.then 메서드로 접근할 수 있다.

.then 안에서 리턴한 값이 Promise면,
Promise의 내부 프로퍼티 result를 다음 .then의 콜백함수의 인자로 받아오고,

Promise가 아니라면
리턴한 값을 .then의 콜백함수의 인자로 받아올 수 있다.

Catch
executor에 작성한 코드가 에러 발생 경우,
reject 함수 호출하고 .catch 메서드로 접근할 수 있다.

Finally
executor에 작성했던 코드들의 정상 처리 여부와 상관없이 .finally 메서드로 접근할 수 있다.
정상이든 에러든 .finally 메서드의 코드는 작동한다.

Promise chaining
Promise chaining이 필요한 경우 = 비동기 작업을 순차적으로 진행해야 하는 경우
이게 가능한 이유는 .then, .catch, .finally 메서드들은
Promise를 리턴하기 때문이다.
따라서, .then을 통해 연결할 수 있고, 에러가 발생할 경우 .catch로 처리하면 된다.

profile
pursue nature

0개의 댓글