될때까지

((비동기 처리방식)) 콜백, Promise, Async/await과 비동기 본문

학습/JavaScript, TypeScript

((비동기 처리방식)) 콜백, Promise, Async/await과 비동기

랖니 2022. 10. 24. 17:23
728x90

🎯 동기와 비동기

동기(Synchronous)

  • 현재 실행중인 코드가 끝나야 다음 코드를 실행한다.
  • 현재 실행중인 작업이 끝날 때 까지 모두 동작 그만!! 다음 task는 대기한다.
  • 장점 : 동기 처리는 코드를 순서대로 실행하기 때문에 실행 순서가 보장된다.
  • 단점 : 현재 실행중인 task가 끝날 때 까지 다음 task는 실행을 못하며, task가 blocking되는 문제가 생긴다.

비동기(Asynchronous)

  • 현재 실행중인 코드가 끝나지 않아도 다음 코드를 실행한다.
  • 비동기 task는 실행하도록 브라우저에게 맡기고 다음 task로 넘어간다.
  • 장점 : 블로킹이 발생하지 않기 때문에 여러개의 일을 처리할 수 있다.
  • 단점 : 코드들의 실행 순서가 보장되지 않는다.
  • 자바스크립트 엔진은 한번에 하나의 task만 실행할 수 있는 싱글 스레드 기반 언어다.
    시간이 많이 걸리는 task의 경우 블로킹이 발생하고 다음 task는 해당 작업이 끝날때 까지 기다려야하므로 효율성이 떨어진다.

* 아래 코드의 실행결과를 예측해보자

setTimeout(function() {
    console.log('1');
}, 0); 

console.log('2');

for (let i = 0; i < 3; ++i) {
    loop();
}

setTimeout(function() {
    console.log('3');
}, 0); 

console.log('4');

function loop() {
    console.log('5');
}

--------> setTimeout은 비동기 처리함수이기 때문에 백그라운드로 작업을 넘기고 다음 코드가 실행된다.
2 -> 5, 5, 5(반복문 돌면서 loop() 실행) -> 4 -> 1, 4(백그라운드에 넘겼던 setTimeout의 실행 결과 출력)

 

🎯 콜백함수

  • 자바스크립트에서는 함수도 객체(object)다.
  • 함수는 다른 함수의 매개변수로 쓰일수도 있고, 어떤 함수의 실행 결과로 반환될 수도 있다. 이를 고차함수라고 한다.
  • 함수의 매개변수가 함수일 때, 매개변수로 받은 함수를 콜백함수(함수 안에서 실행되는 또 다른 함수라고 생각하자)라고 한다.
  • 콜백함수를 사용하면 비동기 처리 방식의 문제점(실행 순서 제어 불가)을 해결할 수 있다.
  • 주의사항 : 콜백함수는 동기처리, 비동기 처리에 모두 쓰인다.
  • 단점 : 비동기 함수가 처리 결과를 가지고 또 비동기 함수를 호출하면 콜백지옥을 일으킨다.
    이로 인해 코드의 가독성이 떨어지고 에러 처리가 어렵다는 단점이 있다.(try catch로 에러 캐치 불가능)

콜백 지옥

 

🎯 Promise

  • ES6에 도입된 비동기 동작을 처리하기 위한 클래스다.
  • 프로미스 클래스를 인스턴스화 해서 프로미스 객체를 만들고 반환된 프로미스로 원하는 비동기 동작을 처리한다.
  • resolve는 성공했을 때 실행할 함수이고 reject는 실패했을 때 실행할 함수다.
  • 참고로 resolve와 reject는 미리 정의하지 않아도 자바스크립트 엔진에서 미리 정의해놓기 때문에 정의하지 않아도 호출에 문제가 없다.
  • resolve와 reject는 여러번 호출해도 첫번째로 호출된 코드만 실행하고 종료된다. 같이 쓰인 경우, 먼저 쓰인 것만 호출되고 종료된다.
// 프로미스 구현하기

let promise = new Promise( function (resolve, reject) {
	// 비동기 로직 작성
    ...생략...
    resolve();  // 에러 발생 안함
    reject();  
})

resolve

  • 성공적으로 실행을 마치면 resolve를 호출하고, resolve로 넘긴 콜백함수의 매개변수를 아래 .then에서 받을 수 있다.
let promise = new Promise(function (resolve, reject) {
	setTimeout(function() {
    	resolve('promise success');
    }, 2000);
});

promise.then(function (msg) {
	console.log(msg);     // 2초 뒤에 promise success 출력
})

reject

let promise = new Promise(function (resolve, reject) {
	setTimeout(function(){
    	// 실패했다고 가정
    	reject('promise fail!');
    }, 2000);
});

promise.then(function(){
	console.log('resolve');
}).catch(function(err){
	console.log('reject', err);    // reject promise fail! 출력
});

Promise의 상태

  • 프로미스 객체는 '상태'를 가지고 있다.
  • pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
  • fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과값을 반환한 상태
  • rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
let promise = new Promise(function(resolve, reject) {
    setTimeout(function() {
      console.log('before', promise)  // before Promise { <pending> }
      reject(1);
      console.log('after', promise);  // after Promise { <rejected> 1 }
    }, 1000);
});

promise.then(function(msg) {
    console.log('resolve', msg);
}, function (msg) {
    console.log('reject', msg);   // reject 1 출력
});

에러처리

  • catch를 사용하여 처리하는 게 가독성이 좋다.
let promise = new Promise(function(resolve, reject) {
    setTimeout(function() {
        reject('에러처리!');
  }, 2000);
});

promise
	.then(function() {
   	})
	.catch(function(err) {
		console.log(err);
	});
function job() {
    return new Promise(function(resolve, reject) {
        reject();
    });
}

job()
    .then(function() {
    console.log(1);
    })
    .then(function() {
    console.log(2);
    })
    .then(function() {
    console.log(3);
    })
    .catch(function() {
    console.log(4);
    })
    .then(function() {
    console.log(5);
    });
    
// 콘솔에 4만 찍힐 것 같았는데 4,5가 찍혔다.
// catch에서 잡고 나서 then은 실행이 되나보다..!
// 맨 아래 then을 => catch로 바꾸면 4만 찍혔다.

Promise.finally()

  • then()과 catch()다음에 적어주는 메소드로, 성공하든 실패하든 항상 실행되는 코드다.

Promise.all()

  • 여러 비동기 함수의 실행이 모두 끝났을 때 받아서 처리하고 싶은 경우에 사용한다.
  • 응답값은 배열로 한꺼번에 반환된다.

 

🎯 Async & await

  • ES8에 도입되었고 async function은 promise를 반환한다.
  • await을 쓰려면 반드시 async로 함수를 선언해야한다.
  • 예외 처리는 try, catch를 사용하여 핸들링할 수 있다.
728x90