늘 겸손하게

JavaScript - Promise, Call back 본문

Programming/JavaScript

JavaScript - Promise, Call back

besforyou999 2022. 7. 16. 17:33

Promise

 

자바스크립트에서 비동기를 간편하게 처리해주는 객체입니다. 비동기는 '특정 코드의 실행이 완료되기를 기다리지 않고 다음 코드를 먼저 수행하는 것을 말합니다'.

 

프로미스는 다음 중 하나의 상태를 가집니다.

 

  1. 대기 ( pending ) : 이행하지도, 거부하지도 않은 초기 상태
  2. 이행 ( fulfilled ) : 연산이 성공적으로 완료
  3. 거부 ( rejected ) : 연산 실패 혹은 오류 발생

 

 

Promise가 필요한 이유

 

자바스크립트에서 비동기 처리는 주로 콜백함수로 이루어집니다.

 

하지만 콜백함수가 많이 중첩될 경우, 가독성이 떨어지고 에러처리도 힘든 콜백지옥에 빠질 수 있습니다. 

 

 

 

 

이러한 콜백함수의 단점을 보완하기 위해 'Promise'를 사용합니다.

 

 

Promise 객체 생성자

 

let promise = new Promise(function(resolve, reject) {
  
});

 

 

객체 생성자의 인자로 주어지는 함수를 executor라 부르고 promise가 실행된 후 작업이 성공적이였다면 resolve를 호출, 작업이 실패였다면 reject를 호출합니다.

 

 

Promise 소비자

 

Promise의 소비 함수로 에러 혹은 결과를 처리할 수 있습니다.

 

Promise 오브젝트는 실행자와 소비 함수 사이의 링크 역할을 합니다. 소비 함수는 ".then", ".catch" 메소드로 구독될 수 있습니다.

 

then

가장 중요하고 근본적인 소비 함수

 

syntax

promise.then(
    function(result) { // 성공 처리 }
    function(error) { // 에러 처리 }
);

 

 

then 함수의 첫 번째 인자는 promise가 resolved 된 경우, 즉 비동기 처리가 정상적으로 이행된 경우 결과를 받습니다.

 

then 함수의 두 번째 인자는 promise가 reject된 경우, 즉 비동기 처리가 실패한 경우 결과를 받습니다.

 

 

<예시 코드>

let promise = new Promise(function(resolve, reject) {

  setTimeout(() => resolve("성공"), 1000);
});

promise.then(
  result => alert(result),  // 1초 뒤 성공 alert
  error => alert(error)     // 실행안됨
);

 

let promise = new Promise(function(resolve, reject) {

  setTimeout(() => reject("실패"), 1000);
});

promise.then(
  result => alert(result),  // 실행 안됨
  error => alert(error)     // 1초 뒤 실패 alert
);

 

 

성공 결과만 처리하고 싶은 경우 인자를 1개만 주면 됩니다.

 

 

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("성공"), 1000);
});

promise.then(alert);    // 1초 뒤 성공 alert 출력

 

catch

에러 처리만 하고 싶은 경우 ".then" 메소드의 첫 번째 인자로 null을 주고 두 번째 인자만 주면 되지만 ".catch" 메소드로 처리하는 방법 또한 있습니다.

 

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("에러")), 1000);
});

// .catch(f) 는 .then(null, f)와 동일
promise.catch(alert); // 1초 뒤 "에러" alert

 

finally

비동기 처리가 성공인지 실패인지에 상관없이 .finally 메소드는 실행되며 그저 마무리 단계에 실행할 코드를 담는 메소드입니다.

하지만 finally가 결과를 "소비"하진 않습니다. 그저 다음 소비 함수에 결과를 전달합니다.

그래서 finally를 then이나 catch보다 먼저 나오게해도 결과가 "소비"되진 않습니다.

 

<예시 코드>

 

new Promise((resolve, reject) => {
  setTimeout(() => resolve("결과"), 2000);
})
  .finally(() => alert("Promise ready")) // 먼저 실행됨
  .then(result => alert(result));        // <-- .then "결과" 출력

 

new Promise((resolve, reject) => {
  throw new Error("에러");
})
  .finally(() => alert("Promise ready")) // 먼저 실행
  .catch(err => alert(err));  // <-- .catch 에러 출력됨

 

 

"finally" 핸들러는 반환값이 없습니다. 반환값을 명시해도 무시됩니다.

 

Callback

 

JavaScript 환경은 비동기 작업 스케줄링을 위한 다양한 함수를 제공합니다. 어떠한 작업을 비동기적으로 처리하는 함수는 작업이 끝났을 때 할 일을 Callback 함수로 전달하는것이 좋습니다.

 

예로 아래와 같은 코드에서 

 

function loadScript(src) {
  let script = document.createElement('script');
  script.src = src;
  document.head.append(script);

}

loadScript('../sample.js');
testFunction();

 

sample.js를 로드하고 sample.js의 함수인 testFunction()을 호출하려하면 오류가 발생합니다. loadScript 함수의 작업이 끝나 sample.js가 document.head에 등록되기도 전에 testFunction을 호출했기 때문입니다. 이러한 문제를 해결하기 위해 Callback 함수를 사용할 수 있습니다.

 

 

function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(script);

  document.head.append(script);
}

 

위 코드에 callback 함수를 매개변수로 선언하고 스크립트에 onload 이벤트가 발생하였을때 실행할 callback을 연결하였습니다.

 

loadScript('../sample.js', function() {
  // the callback runs after the script is loaded
  testFunction(); // so now it works
  ...
});

 

위와 같이 callback을 이용해 스크립트 파일의 정상적으로 document에 등록이 완료가 된 후 testFunction을 호출할 수 있습니다.

 

 

Promise와 Callback 차이

 

둘 다 비동기 작업 처리와 관련이 있지만 차이점이 존재합니다.

 

Promise는 Callback과 다르게 resolve, reject 콜백이 존재하여 비동기 작업이 성공적이였을 경우(resolve), 비동기 작업이 실패하거나 오류가 발생한 경우(reject)를 각각 용이하게 코딩할 수 있습니다.

 

하지만 Callback은 비동기 결과의 처리를 콜백 외부에서는 불가능하여 콜백 내부에서만 할 수 있고 에러 처리 또한 까다롭습니다.

 

 

 

 

 

출처

https://javascript.info/promise-basics