[JavaScript] 동기, 비동기, Promise

SungWoo·2024년 8월 19일

자바스크립트 공부

목록 보기
31/42
post-thumbnail

자바스크립트는 싱글 스레드를 가지며, 이는 단 하나의 실행 컨텍스트 스택(Call Stack)을 가진다는 말과 일맥상통한다.

싱글 스레드(Single Thread): 한 번에 하나의 테스크만 실행 가능
↪ 처리에 시간이 걸리는 테스크를 실행하는 경우 블로킹(작업 중단)이 발생함.


동기, 비동기

1. 동기

  • 순서대로 동작
  • 현재 진행중인 테스크가 종료할 때까지 다음에 실행될 테스크가 대기하는 방식

2. 비동기

  • 진행중인 테스크가 종료될 때까지 기다리지 않고 바로 다음 동작이 실행되는 것
  • 현재 실행 중인 테스크가 종료되지 않은 상태라 해도 다음 테스크를 곧바로 진행하는 방식
    ↪ "다른 주기를 가지고 실행"
// ex1. 
let a = 0;
 
// setTimeout(비동기 함수)
setTimeout(function () {
    a = 100; // 할당 안됨
    console.log('1st');
    setTimeout(function () {
        console.log('2nd');
        setTimeout(function () {
            console.log('3rd');
            // render('rendering...');
        }, 1000);
    }, 2000);
}, 3000);

console.log('HELLO'); 
console.log(a); 

// 출력
// HELLO
// 0
// 1st
// 2nd
// 3rd

// setTimeout 함수는 생성된 타이머를 식별할 수 있는 고유한 타이머 id를 반환
// 비동기 함수인 setTimeout 함수는 콜백 함수의 처리 결과를 외부로 반환하거나 상위 스코프의 변수에 할당하지 못한다.

Promise

: 비동기를 간단하게 처리할 수 있도록 도와주는 "객체"

  • 비동기 처리를 수행할 콜백 함수(resolve(성공), reject(실패))를 인수로 전달받음
  • 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과값을 나타냄
  • 정상적으로 기능이 수행되면 성공 메세지와 함께 처리된 결과값을 전달해줌, 기능을 수행하다 문제가 발생하면 에러를 전달해줌

"미래의 어떤 시점에 결과를 제공하겠다는 약속을 반환"

※ 유의사항
: Promise를 생성하게 되면 excutor(콜백함수)가 바로 실행됨

// ex1(Promise).
const render = () => console.log('rendering...');

const timeout = (time) => (resolve, reject) => {
    setTimeout(() => resolve('promise: '), time);
};

const delay = (time) => new Promise(timeout(time));

delay(3000)
    .then((msg) => {
        console.log(msg + '1st');
        return delay(2000);
    })
    .then((msg) => {
        console.log(msg + '2nd');
        return delay(1000);
    })
    .then((msg) => {
        console.log(msg + '3nd');
        render();
    });

// 출력
// promise: 1st
// promise: 2nd
// promise: 3rd
// rendering...

ex. 데이터를 불러오는 경우

사이트의 데이터를 전부 불러올 때 까지 기다리게 된다면 사용자들은 불편함을 느낌

const response = fetch(`myImage.png`)
const blob = response.blob();
// display your image blob in the UI somehow
  • 이미지를 불러오고 불러온 것을 확인한 후에 응답을 처리하게 해야 함
  • 이미지가 불러와지기 전에 응답을 출력하게하면 오류가 뜸

1. state

1) pending(대기): 초기 상태
2) fulfilled(이행): 성공적으로 완료된 상태
3) rejected(실패): 실패 상태
4) settled: 비동기 처리가 수행된 상태

2. Promise chainning

  • 프로미스는 체이닝을 이용하여 결과를 변경하고 에러처리를 할 수 있음
// ex2. Promise chainning
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok');
        // reject('error');
    }, 1000);
});

promise.then(
    // 정상적으로 작동 시 then 메서드에서 반환값 처리
    function (result) {
        console.log(result)
    }
).catch(
    // 실패하게되면 catch 메서드에서 오류를 잡음
    function (error) {
        console.log(error)
    }
).finally(() => {
    console.log("성공이든 실패든 무조건 실행")
});

// 출력
// ok
// 성공이든 실패든 무조건 실행
// ex3. 
const getApple = () =>
    new Promise((resolve, reject) => {
        setTimeout(() => resolve('🍎'), 1000);
    });

const getBanana = (apple) =>
    new Promise((resolve, reject) => {
        setTimeout(() => resolve(`${apple} -> 🍌`), 1000);
    });

const getTrain = (banana) =>
    new Promise((resolve, reject) => {
        setTimeout(() => resolve(`${banana} -> 🚆`), 1000);
    });

// getApple()
//     .then((banana) => getBanana(banana))
//     .then((train) => getTrain(train))
//     .then((result) => console.log(result));

// 전달하는게 하나일 경우 줄여서 사용가능
getApple()
    .then(getBanana)
    .then(getTrain)
    .then(console.log);
    
// 출력
// 🍎 -> 🍌 -> 🚆
// ex4. 에러처리를 이용하여 결과 변경
const getApple = () =>
    new Promise((resolve, reject) => {
        setTimeout(() => resolve('🍎'), 1000);
    });

const getBanana = (apple) =>
    new Promise((resolve, reject) => {
        setTimeout(() => reject(new Error(`${apple} -> 🍌`)), 1000);
    });

const getTrain = (banana) =>
    new Promise((resolve, reject) => {
        setTimeout(() => resolve(`${banana} -> 🚆`), 1000);
    });

getApple()
    .then(getBanana)
    .catch((error) => {
        return '🍍';
    })
    .then(getTrain)
    .then(console.log);
    
// 출력
// 🍍 -> 🚆

3. Promise.all, Promise.race

프로미스를 요소로 갖는 배열 등의 이터러블을 인수로 전달받음

1) Promise.all

  • 전달받은 모든 프로미스가 fulfilled 상태가 되는 것을 기다렸다가 처리 결과를 배열에 저장해 새로운 프로미스를 반환
  • 여러 개의 비동기 처리를 "병렬 처리"할 때 사용

    ※ 특징
    ① 순서대로 실행되지만, 앞의 함수를 기다리지 않는다.
    ② 처리 순서가 보장된다.
    ⑤ 에러가 나면 그 즉시 에러를 반환한다.
    ↪ 조건 중 하나라도 누락되면 페이지를 보여주면 안될 때 사용가능

// ex5. Promise.all (가장 긴 task 기준)
function timer(time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(time);
        }, time);
    });
}

console.time('all');
Promise.all([timer(1000), timer(2000), timer(3000)]).then((result) => {
    console.log('result', result);
    console.timeEnd('all');
});

2) Promise.race

  • 가장 먼저 fulfilled 된 프로미스의 처리 결과를 resolve하는 새로운 프로미스를 반환
// ex6. Promise.race (가장 짧은 task 기준)
function timer(time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(time);
        }, time);
    });
}

console.time('race');
Promise.race([timer(1000), timer(2000), timer(3000)]).then((result) => {
    console.log('result', result);
    console.timeEnd('race');

정리

  1. 배열에 있는 모든 promise가 트리거가 된다.(약간의 차이 있음)
  2. Promise.all : 마지막 기준
  3. Promise.race : 처음 기준
profile
어제보다 더 나은

0개의 댓글