본문 바로가기
front-end/JavaScript

[JS]08-async 동기와 비동기 (callback, promise, async, await)

by -제이리 2022. 3. 8.
728x90
320x100
JavaScript is synchronous. 자바스크립튼 동기적이다.
-호이스팅이 된 이후부터 코드가 위에서부터 하나하나씩 동기적으로 실행된다
(호이스팅: var, 함수선언들이 제일 위로 올라가는것)
 

<동기적인 예>

console.log('1');
console.log('2');
console.log('3');

// 1-2-3 차례로 실행

 

<비동기적인 예>

console.log('1');
setTimeout(() => console.log('2');, 1000);//1초뒤에 실행하라는 명령을 내림
console.log('3' );

// 1-3-2 순서로 나옴

 


callback

synchronous callback (동기 콜백)

 function printImmediately(print){
     print();
 }
printImmediately(()=> console.log('hello'));

 

asynchronous callback (비동기 콜백)

 function printWithDelay(print, timeout){
     setTimeout(print, timeout);
 }
 printWithDelay(()=>console.log('async callback'),2000);

promise

Promise란? 자바스크립트 안에 내장되어있는 오브젝트.
 
-비동기적인 것을 수행할때 콜백함수 대신에 유용하게 쓸수 있는 오브젝트이다.

 

<두가지 포인트를 알고 공부하기>
 
첫번째 포인트: state(상태)-무거운 오퍼레이션을 수행하고 있는 중인지, 이미 수행이 완료되어서 수행에 성공했는지 실패했는지 이런 상태에 대해서 이해햐기
 
두번째 포인트: 프로듀서(정보제공자)와 컨슈머의 차이점에 대해서 이해하기
 
[state] 오퍼레이션이 수행중일땐 pending -> 오퍼레이션 완료시 성공적이면 fullfilled 상태 / 실패하면 rejected 상태

 

producer vs consumer
 
 
1. producer
 
-유의할점 : 새로운 promise가 만들어질때는 우리가 전달한 excutor 라는 콜백함수가 바로 실행이 된다.
// Promise는 클래스이기 떄문에 new 키워드를 사용하여 오브젝트를 생성할수 있다.  
const promise = new Promise((resolve, reject)=>{ 
    //doing some heavy work(network, read files)
    console.log('doing something...');
    setTimeout(()=>{
        // resolve('Tom');
        reject(new Error('no network'));
    }, 2000)
});

 

2. Consumers: then, catch, finally 를 이용해서 값을 받아올 수 있다.
 
-then(성공했을때): promise가 정상적으로 수행이 잘 되어서 최종적으로 resolve의 콜백함수를 통해서 value의 파라미터로 전달되어져서 들어온다.
 
-catch (실패해서 에러났을때)
 
-finally: 성공하든 실패하든 무조권 마지막에 호출됨.
promise
.then((value)=> {
    console.log(value); // -> 성공시 doing something... 출력
})
.catch(error => {
    console.log(error); // -> 실패시 no network 출력
})
.finally(()=>{
    console.log('finally'); // -> 성공하던 실패하던 무조건 finally 출력
});

 

3. promise chaining
const fetchNumber = new Promise((resolve, reject)=>{
    setTimeout(()=> resolve(1), 1000);
});

fetchNumber
.then(num => num *2)
.then(num => num *3)
.then(num => {
    return new Promise((resolve, reject)=>{
        setTimeout(()=> resolve(num -1), 1000)
    });
})
.then(num => console.log(num)); // 5



4. Error Handling
 
const getHen = () =>
  new Promise((resolve, reject)=>{
    setTimeout(()=>resolve('🐓'),1000);
});
const getEgg = hen =>
  new Promise((resolve, reject)=>{
      setTimeout(()=> reject(new Error(`error! ${hen} => 🥚`)), 1000);
  });
const cook = egg =>
  new Promise((resolve, reject)=>{
      setTimeout(()=> resolve(`${egg} => 🍳`), 1000);
  });
  
  
getHen()
.then(hen => getEgg(hen))
.then(egg => cook(egg))
.then(meal => console.log(meal));

//더 간단하게 쓰는법 (한가지만 받아서 고대로 쓰는경우는 화살표 생략가능)
getHen() 
.then(getEgg)
.catch(error => {  // 문제발생시 대체값 넣을 수 있음
    return '🍗'  
})
.then(cook)
.then(console.log)
.catch(console.log); //"🍗 => 🍳"

 


콜백지옥과 promise를 통한 해결법

 

 


async & await

-promise를 깔끔하게 사용할 수 있는 방법 (무조건 대체하는 건 x, 상황에 따라 맞는것 쓰기)

1. async
 
-function 앞에 async를 붙여준다
이렇게 하면 번거롭게 프로미스를 쓰지 않아도 자동적으로 함수안에 있는 코드블럭들이 프로미스로 변환이 된다.

 

async function fetchUser() {
    // (네트워크통신을 해서 백엔드에서 10초정도 걸리는 데이터를 가져온다 가정하면..)
     return 'Tom';
}

const user = fetchUser();
user.then(console.log);
console.log(user); // -> Tom

 

2. await
 
-async가 붙은 함수 안에서만 쓸수 있다.

 

function delay(ms) { // ms : 밀리세컨드 (초)
    return new Promise(resolve, setTimeout(resolve, ms));    
}

async function getApple() {
    await delay(3000); //3초기다린 뒤
    return 'apple'; // apple반환해!
}

async function getBanana() {
    await delay(3000);
    return 'Banana';
}

//위에 있는 apple과 banana를 동시에 불러오고 싶을땐

async function pickFruits(){
    const appel = await getApple();
    const banana = await getBanana();
    return `${appel} + ${banana}`;
}

pickFruits().then(console.log); //-> 6초뒤에 'apple + banana' 이 실행된다.

//하지만 apple과 banana는 서로 연관도 없는데 위에서부터 하나씩 불러오기때문에 오래걸린다는 단점이 있음

//이걸 병렬적으로 사용하려면 이와같이 쓸수 있지만
async function pickFruits(){
    const appelPromise = getApple();
    const bananaPromise = getBanana();
    const appel = await appelPromise;
    const banana = await bananaPromise;
    return `${appel} + ${banana}`;
}

pickFruits().then(console.log); // -> 3초뒤에 동시에 실행됨

//코드가 더럽다.. 개선해서 나온게 아래의 promise API의 .all 이 있다.

 

3. useful APIs (.all)
 
-promise의 배열을 전달하게 되면 모든 promise들이 병렬적으로 다 받을때까지 모아준다.
 
function pickAllFruits(){
    return Promise.all ([getApple(), getBanana()]) //배열이 전달받아지면
    .then(fruits => fruits.join(' + ')); // 받은 배열을 join으로 묶어준다.
}

pickAllFruits().then(console.log); // -> 3초뒤 동시실행

 

728x90
320x100

댓글