JavaScript로 자신만의 Promise 구현

Promise는 우리 모두가 애플리케이션에서 여러 번 사용한 JavaScript의 가장 기본적인 개념 중 하나이지만 자체 Promise API를 구현할 수 있습니까?

보기보다 복잡하지 않으니 걱정하지 마세요.

이 포스트에서 우리는 기본적인 Promise API를 직접 구현할 것입니다.

약속이란 무엇입니까?



The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.



다음 세 가지 상태 중 하나일 수 있습니다.
  • PENDING, 작업이 진행 중일 때 초기 상태
  • FULFILLED, 작업이 성공했음을 정의합니다
  • .
  • REJECTED, 작업 실패를 나타냅니다
  • .

    Note: A promise is said to be settled when it is either fulfilled or rejected. (we are going to use this term a lot in this article)



    약속을 어떻게 사용합니까?



    먼저 약속을 구현하기 위한 골격, 기본적으로 필요한 입력, 노출하는 메서드를 살펴보겠습니다.

    콜백을 받는 생성자 함수와 then, catch 및 finally와 같은 메서드가 있습니다.

    const promise = new Promise((resolve, reject) => {
       /*
         Your code logic goes here and you call  resolve(value)
         or reject(error) to resolve or reject the promise
       */ 
    })
    
    promise.then((value) => {
       // Code logic on success of an operation
    }).catch(error => {
      // Code logic on failure of an operation
    }).finally(() => {
      // Code logic to be executed after completion of operation
    })
    


    1. 스켈레톤 정의



    Promise 클래스 MyPromise를 정의하는 것으로 시작합니다.

    생성자에 정의된 속성은 다음과 같습니다.


  • state : PENDING , FULFILLED 또는 REJECTED
  • 일 수 있습니다.
  • handlers : then, catch, finally 메서드의 콜백을 저장합니다. (핸들러는 Promise가 해결되었을 때만 실행됩니다.)
  • value : 값을 확인하거나 거부합니다.

  • 참고: Promise는 생성되자마자 실행됩니다. 즉, Promise 콜백 함수는 rejectresolve 메서드가 매개변수로 전달된 생성자 내부에서 호출됩니다.

    
    const STATE = {
      PENDING: 'PENDING',
      FULFILLED: 'FULFILLED',
      REJECTED: 'REJECTED',
    }
    class MyPromise {
        constructor(callback) {
          // Initial state of Promise is empty
          this.state = STATE.PENDING;
          this.value = undefined;
          this.handlers = [];
          // Invoke callback by passing the _resolve and the _reject function of our class
          try {
            callback(this._resolve, this._reject);
          } catch (err) {
            this._reject(err)
          }
        }
    
        _resolve = (value) => {}
    
        _reject = (error) => {}
    
        then(onSuccess, onFail) {
        }
    
        catch(onFail) {
        }
    
        finally(callback) {
        }
    }
    
    


    2. _resolve() 및 _reject() 메서드 구현


    _resolve() 또는 _reject()는 promise의 state를 각각 FULFILLED 또는 REJECTED로 설정하고 value 속성을 업데이트하고 연결된 핸들러를 실행합니다.

    Note: Nothing happens if we try to call _resolve() or _reject() on an already settled Promise.



      _resolve = (value) => {
        this.updateResult(value, STATE.FULFILLED);
      }
    
      _reject = (error) => {
        this.updateResult(error, STATE.REJECTED);
      }
    
      updateResult(value, state) {
        // This is to make the processing async
        setTimeout(() => {
          /*
            Process the promise if it is still in a pending state. 
            An already rejected or resolved promise is not processed
          */
          if (this.state !== STATE.PENDING) {
            return;
          }
    
          // check is value is also a promise
          if (isThenable(value)) {
            return value.then(this._resolve, this._reject);
          }
    
          this.value = value;
          this.state = state;
    
          // execute handlers if already attached
          this.executeHandlers();
        }, 0);
      }
    


    위의 코드에서 isThenable(value)가 무엇인지 궁금하십니까?

    다른 Promise로 Promise가 해결/거부된 경우에는 완료될 때까지 기다렸다가 현재 Promise를 처리해야 합니다.

    isThenable() 함수 구현


    isThenable 함수는 값이 MyPromise의 인스턴스인지 또는 then 함수를 포함하는 객체인지 확인합니다.

    function isThenable(val) {
      return val instanceof MyPromise;
    }
    
    // or
    
    function isThenable(value) {
      if (typeof value === "object" && value !== null && value.then && typeof value.then === "function") {
        return true;
      }
      return false;
    }
    


    3. then() 메소드 구현


    then() 메서드는 두 개의 인수를 콜백onSuccessonFail로 사용합니다. onSuccess는 약속이 이행되면 호출되고 onFail는 약속이 거부되면 호출됩니다.

    “Remember that Promises can be chained”.
    The essence of Promise chaining is that the then() method returns a new Promise object. That is how promises can be chained. This is especially useful in scenarios where we need to execute two or more asynchronous operations back to back, where each subsequent operation starts when the previous operation succeeds, with the result from the previous step.


    then()에 전달된 콜백은 handlers 함수를 사용하여 addHandlers 배열에 저장됩니다. 핸들러는 promise가 처리될 때 실행될 객체{onSuccess, onFail}입니다.
    then() 구현은 다음과 같습니다.

    then(onSuccess, onFail) {
      return new MyPromise((res, rej) => {
          this.addHandlers({
            onSuccess: function(value) {
              // if no onSuccess provided, resolve the value for the next promise chain
              if (!onSuccess) {
                return res(value);
              }
              try {
                return res(onSuccess(value))
              } catch(err) {
                return rej(err);
              }
            },
            onFail: function(value) {
              // if no onFail provided, reject the value for the next promise chain
              if (!onFail) {
                return rej(value);
              }
              try {
                return res(onFail(value))
              } catch(err) {
                return rej(err);
              }
            }
          });
      });
    }
    
    addHandlers(handlers) {
      this.handlers.push(handlers);
      this.executeHandlers();
    }
    
    executeHandlers() {
      // Don't execute handlers if promise is not yet fulfilled or rejected
      if (this.state === STATE.PENDING) {
        return null;
      }
    
      // We have multiple handlers because add them for .finally block too
      this.handlers.forEach((handler) => {
        if (this.state === STATE.FULFILLED) {
          return handler.onSuccess(this.value);
        } 
        return handler.onFail(this.value);
      });
      // After processing all handlers, we reset it to empty.
      this.handlers = [];
    }
    
    
    


    4. catch() 메소드 구현


    catch()then()를 사용하여 구현됩니다. then() 콜백을 onSuccess로 사용하여 null 메소드를 호출하고 onFail 콜백을 두 번째 인수로 전달합니다.

    
        /*
            Since then method take the second function as onFail, 
            we can leverage it while implementing catch
        */
        catch(onFail) {
          return this.then(null, onFail);
        }
    


    5. finally() 메서드 구현


    finally() 메서드 구현을 시작하기 전에 먼저 동작을 이해하도록 합시다.

    MDN 문서에서:

    The finally() method returns a Promise. When the promise is settled, i.e either fulfilled or rejected, the specified callback function is executed. This provides a way for code to be run whether the promise was fulfilled successfully or rejected once the Promise has been dealt with.

    The finally() method is very similar to calling .then(onFinally, onFinally) however there are a couple of differences:

    When creating a function inline, you can pass it once, instead of being forced to either declare it twice, or create a variable for it

    Unlike Promise.resolve(2).then(() => {}, () => {}) (which will be resolved with undefined), Promise.resolve(2).finally(() => {}) will be resolved with 2.

    Similarly, unlike Promise.reject(3).then(() => {}, () => {}) (which will be fulfilled with undefined), Promise.reject(3).finally(() => {}) will be rejected with 3.


    finally() 메서드는 이전 fulfilled 또는 rejected 값으로 정산될 약속을 반환합니다.

        // Finally block returns a promise which fails or succeedes with the previous promise resove value
        finally(callback) {
          return new MyPromise((res, rej) => {
             let val;
             let wasRejected;
             this.then((value) => {
               wasRejected = false;
               val = value;
               return callback();
             }, (err) => {
               wasRejected = true;
               val = err;
               return callback();
             }).then(() => {
               // If the callback didn't have any error we resolve/reject the promise based on promise state
               if(!wasRejected) {
                 return res(val);
               } 
               return rej(val);
             })
          })
        }
    


    아래 codepen에서 전체 코드 구현을 확인하십시오.



    요약



    우리는 Promise의 기본 구현을 에뮬레이트했습니다. 인스턴스 메소드인 then() , catch() , finally() 메소드보다 훨씬 더 많은 것이 있습니다. 앞으로의 게시물에서 다루려고 하는 정적 메서드도 있습니다.

    나는 당신이 기사를 즐겼기를 바랍니다.

    읽어 주셔서 감사합니다...

    제안이나 질문이 있으시면 언제든지 댓글이나

    좋은 웹페이지 즐겨찾기