Promise

Feb 16th 2020 by jyoon
  • javascript info 팬이 가수의 앨범을 사기위해서 아무런 정보 없이 대기 하기 보다는 가수가 앨범을 다만들면 팬들에게 그 소식을 알려 팬이 가수의 앨범을 살수 있다 라는 설명으로 promise로 설명 하고 있습니다.

    목차
    # executor: promise
    # 소비자: then, catch, finally
    # 예시: loadScript

executor: promise

var promise = new Promise(function(resolve, reject) {
  // executor (제작 코드, "가수")
  // resolve, reject 둘중 하나 함수 반드시 호출
})
  • executor는 아래 콜백 중 하나를 반드시 호출해야 합니다.

    • resolve(value): 성공적으로 끝난 경우 결과를 value param으로 전달하며 resolve 호출
    • reject(error): 에러 발생시 에러 객체를 error param으로 전달하며 reject 호출
  • new Promise(executor)의 callback 'resolve(value)', 'reject(error)'의 state, result 상태
  • new Promise 생성자가 반환하는 promise 객체는 다음과 같은 내부 프로퍼티를 갖습니다.

    • state: 처음엔 "pending"(보류)이었다 resolve가 호출되면 "fulfilled", reject가 호출되면 "rejected"로 변합니다.
    • result: 처음엔 undefined이었다, resolve(value)가 호출되면 value로, reject(error)가 호출되면 error로 변합니다.
  • 성공일때 예제(resolve)

    var promise = new Promise(function(resolve, reject) {
    // 프라미스가 만들어질 때 executor 함수는 자동으로 실행됩니다.
    
    setTimeout(() => resolve("done"), 1000)
    // 1초 뒤에 일이 성공적으로 끝났다는 신호와 함께, result가 'done'이 됩니다.
    })
  • 실패일때 예제(reject)

    var promise = new Promise(function(resolve, reject) {
    // 1초 뒤에 에러와 함께 실행이 종료되었다는 신호를 보냅니다.
    setTimeout(() => reject(new Error("에러 발생!")), 1000)
    })
  • resolve/reject 함수 호출 규칙

    • executor에 의해서 끝난일은 resolve, reject중 첫번째로 오는 것만 수행됩니다.
    • 나머지는 무시

      var promise = new Promise(function(resolve, reject) {
      resolve("done")
      
      reject(new Error("…")) // 무시됨
      setTimeout(() => resolve("…")) // 무시됨
      })

소비자: then, catch, finally

프라미스 객체는 executor(‘제작 코드’ 혹은 ‘가수’)와 결과나 에러를 받을 소비 함수(‘팬’)를 이어주는 역할을 합니다. 소비함수는 .then, .catch, .finally 메서드를 사용해 등록(구독)됩니다.

then

executor가 성공, 실패 한경우를 모두 handling 할 수 있다. then parameter에 두개를 전달해줄 수 있는데 첫번째는 param에 성공 handling function, 두번째는 param에 실패 hanlding function을 전달할 수 있다.

  • executor가 성공적으로 수행한 경우

    • POINT 주석 참고

      var promise = new Promise(function(resolve, reject) {
      setTimeout(() => resolve("done!"), 1000)
      })

    // POINT: resolve 함수는 .then의 첫 번째 함수(인수)를 실행합니다. promise.then( result => alert(result), // 1초 후 "done!"을 출력 error => alert(error) // 실행되지 않음 )

    - 아래 와 같이 성공했을때는 작성 가능하다
    - then function에 parameter 한개만 전달해주면 된다.
      ```js
      var promise = new Promise(resolve => {
        setTimeout(() => resolve("done!"), 1000)
      })
    
      promise.then(alert) // 1초 뒤 "done!" 출력
  • executor가 에러로 필해한 경우

    • POINT 주석 참고

      var promise = new Promise(function(resolve, reject) {
      setTimeout(() => reject(new Error("에러 발생!")), 1000)
      })

    // POINT: reject 함수는 .then의 두 번째 함수를 실행합니다. promise.then( result => alert(result), // 실행되지 않음 error => alert(error) // 1초 후 "Error: 에러 발생!"를 출력 )

catch

executor 수행 결과 중 error만 다루고 싶을 때 사용

  • 예시

    • POINT 주석 참고
    • .catch(f)는 문법이 간결하다는 점만 빼고 .then(null,f)과 완벽하게 동일

      var promise = new Promise((resolve, reject) => {
      setTimeout(() => reject(new Error("에러 발생!")), 1000)
      })
      // POINT
      //  : .catch(f)는 promise.then(null, f)과 동일하게 작동합니다
      promise.catch(alert) // 1초 뒤 "Error: 에러 발생!" 출력

finally

try... catch에 finally가 있는 것 처럼 promise에도 finally가 있다.

  • finally 예1
    : finally 이후 then을 통해 resolve 결과를 then으로 전달

    new Promise((resolve, reject) => {
    setTimeout(() => resolve("result"), 2000)
    })
    // 성공, 실패 여부와 상관없이, 프라미스가 처리되면 실행됨
    .finally(() => alert("Promise ready"))
    .then(result => alert(result)) // <-- .then에서 result를 다룸
  • finally 예2
    : finally 이후 cath를 통해

    new Promise((resolve, reject) => {
    throw new Error("에러 발생!")
    // setTimeout(()=>{ reject(new Error('error'))}, 1000); // executor 처리 시점만 조금 다르고 같은 결과
    })
    // 성공, 실패 여부와 상관없이, 프라미스가 처리되면 실행됨
    .finally(() => alert("Promise ready"))
    .catch(err => alert(err)) // <-- .catch에서 에러 객체를 다룸

예시: loadScript

  • callback에서 loadScript 설명에서 callback hell을 해결 할 수 있는 방법이 promise라고 했는데 그 방법을 소개하겠다.
  • promise로 구현한 loadScript

    function loadScript(src) {
    return new Promise(function(resolve, reject) {
      var script = document.createElement("script")
      script.src = src
    
      script.onload = () => resolve(script)
      script.onerror = () =>
        reject(new Error(`${src}를 불러오는 도중에 에러가 발생함`))
    
      document.head.append(script)
    })
    }
  • 사용 방법

    var promise = loadScript(
    "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"
    )
    
    promise.then(
    script => alert(`${script.src}을 불러왔습니다!`),
    error => alert(`Error: ${error.message}`)
    )
    
    promise.then(script => alert("또다른 핸들러..."))
    • 설명
    • loadScript(script)로 스크립트를 읽고, 결과에 따라 그다음(.then)에 무엇을 할지에 대한 코드를 작성하면 되죠.
    • 비교
    • 콜백으로 구현시에 loadScript(script, callback)를 호출할 때, 함께 호출할 callback 함수가 준비되어 있어야 합니다.
      loadScript를 호출하기 이전에 호출 결과로 무엇을 할지 미리 알고 있어야 했다.

참고