簡易版のPromiseを実現します.

11909 ワード

前言
PromiseはJavaScriptの中で非同期の操作に非常に重要な構造関数ですが、どうやって実現されますか?
以下はオリジナルJavaScriptで簡易版のPromiseを実現し、以下のコードを実現します.
function Promise(???){
    ???
    return ???
}

var promise = new Promise(function(resolve, reject) {
  setTimeout(() => {
    resolve('hello');
  }, 3000);
});

promise.then(
  val => {
    console.log('     ', val);//          hello
  },
  val => {
    console.log('     ', val);
  }
);
実現する
Promiseを使ったことがある友達はすべて知っていて、Promiseは関数fnを受け取ってそしてそれを呼び出して、Promiseはまた状態(pending、reolved、reject)の属性値statusを表すべきで、1つのvalueストアthen回調の時に伝える値、もう一つのthen方法があります.他の方法はしばらく考えません.
function Promise(fn) {
  //   status   peding
  let status = 'pending';
  //   then    
  let value;
  //   then      
  let onResolvedCallback;
  let onRejectedCallback;

  //   fn   resolve   
  function resolve(data) {
    //  status    fulfilled
    status = 'fulfilled';
    //        
    value = data;
    toDoThen(onResolvedCallback, onRejectedCallback);
  }

  //   fn   reject   
  function reject(data) {
    //  status    rejected
    status = 'rejected';
    //        
    value = data;
    toDoThen(onResolvedCallback, onRejectedCallback);
  }

  //   resolve   reject      
  function toDoThen(onFulfill, onReject) {
    //    fulfilled   ,      then       
    if (status === 'fulfilled') {
      onFulfill && onFulfill(value);
      status = 'pending';
    //    rejected   ,      then       
    } else if (status === 'rejected') {
      onReject && onReject(value);
      status = 'pending';
    //       peding,   resolve   reject     ,     then         ,  resolve   reject       
    } else {
      onResolvedCallback = onFulfill;
      onRejectedCallback = onReject;
    }
  }

  //  then         toDoThen
  this.then = function(onFulfill, onReject) {
    toDoThen(onFulfill, onReject);
  };

  //   fn
  fn(resolve, reject);

  return this;
}
テスト
var promise = new Promise(function(resolve, reject) {
  setTimeout(() => {
    resolve('hello');
  }, 3000);
});

promise.then(
  val => {
    console.log('     ', val);
  },
  val => {
    console.log('     ', val);
  }
);
実行時には「初めての成功」と「ハロー」が印刷されます.
連鎖式then
以下のコードを実装する場合、第二のthenの実行関数は、第一のthenの戻り値のブール値に基づいて決定される.
promise
  .then(
    val => {
      console.log('     ', val);
      return 'world';
    },
    val => {
      console.log('     ', val);
      return false;
    }
  )
  .then(
    val => {
      console.log('     ', val);
    },
    val => {
      console.log('     ', val);
    }
  );
ここでチェーンthenを実現するには、thenが戻ってくるのもPromiseオブジェクトでなければならず、最初のthenの戻り値から次のthenがどの関数を呼び出すかを決定する.
function Promise(fn) {
  //   status   peding
  let status = 'pending';
  //   then    
  let value;
  //   then      
  let onResolvedCallback;
  let onRejectedCallback;

  //   fn   resolve   
  function resolve(data) {
    //  status    fulfilled
    status = 'fulfilled';
    //        
    value = data;
    toDoThen(onResolvedCallback, onRejectedCallback);
  }

  //   fn   reject   
  function reject(data) {
    //  status    rejected
    status = 'rejected';
    //        
    value = data;
    toDoThen(onResolvedCallback, onRejectedCallback);
  }

  //   resolve   reject      
  function toDoThen(onFulfill, onReject) {
    //    fulfilled   ,      then       
    if (status === 'fulfilled') {
      onFulfill && onFulfill(value);
      status = 'pending';
    //    rejected   ,      then       
    } else if (status === 'rejected') {
      onReject && onReject(value);
      status = 'pending';
    //       peding,   resolve   reject     ,     then         ,  resolve   reject       
    } else {
      onResolvedCallback = onFulfill;
      onRejectedCallback = onReject;
    }
  }

  this.then = function(onFulfill, onReject) {
    return new Promise((resolve, reject) => {
      toDoThen(
        val => {
          let result = onFulfill(val);
          //      then            then    
          result ? resolve(result) : reject(result);
        },
        err => {
          let result = onReject(err);
          //      then            then    
          result ? resolve(result) : reject(result);
        }
      );
    });
  };

  //   fn
  fn(resolve, reject);

  return this;
}
実行してみます
var promise = new Promise(function(resolve, reject) {
  setTimeout(() => {
    resolve('hello');
  }, 3000);
});

promise
  .then(
    val => {
      console.log('     ', val);
      return 'world';
    },
    val => {
      console.log('     ', val);
      return false;
    }
  )
  .then(
    val => {
      console.log('     ', val);
    },
    val => {
      console.log('     ', val);
    }
  )
プログラムは「初めての成功」と「二回目の成功」を順次出力します.
これで簡単版のPromiseが実現しました.もちろん細かいところまでは考えられませんでしたが、実現地は非常に粗いです.