Promise 小小的總結與實現

江小魚發表於2018-05-20

Promise 是一種非同步流程的控制手段

  • Promise 只有一個引數 excutor(執行器),預設 new 時候就會呼叫執行器
  • promise 可以解決非同步問題,then 方法是非同步的,執行器 excutor 是同步的。
console.log(0);
new Promise((resolve, reject) => {
  console.log(1);
  resolve(4);
}).then(data => {
  console.log(data);
})
console.log(2);
// 0 1 2 4
複製程式碼
  • 三個狀態

    • pending 等待
    • resole 成功
    • reject 失敗
    • 只有狀態是等待的時候,才可以改變狀態,如果 promsie 成功了就不能失敗,相反失敗了也不能成功

image

  • 解決回撥地獄

promise 支援鏈式呼叫,鏈式呼叫返回的並不是 this 而是一個新的 promise

舉個例子: 本次的結果是下次請求的資料。

回撥方式

let fs = require('fs');
fs.readFile('1.text', 'utf8', function(error, data) {
  if(error) {
    console.log('1.error', error);
  }
  console.log('1.data', data);
  // 讀取2.text
  fs.readFile(data, 'utf8', function(error, data) {
    if(error) {
      console.log('2.error', error);
    }
    console.log('2.data', data);
    // .... 繼續巢狀
  });
});
複製程式碼

promise 版本

let fs = require('fs');
function read(url) {
  return new Promise((resolve, reject) => { 
    fs.readFile(url, 'utf8', function(error, data) {
      if (error) {
        reject(error);
      }
      resolve(data);
    })
  });
}
read('1.text').then((data) => {
  console.log(data);
  return read(data); // 在次 扔出一個新的 promise 例項
}, (error) => {
  console.log(error);
}).then((data) => {
  console.log('2.data', data);
}, (error) => {
  console.log('2.error', error);
});
複製程式碼
  • promise 可以支援多個併發請求

Promise.all() 併發處理,有一個失敗就失敗

Promise.all([read('1.text'), read('2.text')]).then(data => {
  console.log(data); // [ '2.text', '我是根據1.text來的' ] 和請求一一對應
});
複製程式碼

Promise.race() 賽跑,只取回來最快的

Promise.race([read('1.text'), read('2.text')]).then(data => {
  console.log(data); // 誰先回來用誰的
});
複製程式碼
  • 語法糖

返回一個成功的 promise

Promise.resolve('123').then(data => {
  console.log(data);
});
複製程式碼

返回一個失敗的 promise

Promise.reject('錯誤了').then(null, error => {
  console.log(error);
})
複製程式碼
  • 流程圖
    image

自己實現 myPromise

  • 如何驗證是否符合 Promise A+ 規範
<!--安裝測試包-->
npm install promises-aplus-tests -g
<!--測試檔案-->
promises-aplus-tests 檔名
複製程式碼
  • 呼叫
let myPromise = require('./promise');
let p = new myPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('哈哈');
  }, 1000);
});

// 返回值是 promise 將 promise 的返回值傳遞到下一個then中(2.then)
// 返回普通值 直接把值傳遞到下一個then (2.then)
// promise then鏈式呼叫 返回的是一個新的promise 

p.then((data) => {
  console.log('1.then')
  console.log('data', data);
  return new Promise((resolve, reject) => {
    resolve(new Promise((resolve, reject) => {
      resolve('我是普通值~');
    }));
  })
}).then((data) => {
  console.log('2.then');
  console.log('2.then:data', data);
}, (error) => {
  console.log('2.then:error', error);
});

複製程式碼
  • 內部實現
// promise then鏈式呼叫 返回的是一個新的promise 
function resolvePromise(promise2, x, resolve, reject) { 
  // 此處的 resolve、reject 是 promise2 中的
  // 判斷 x 值是個啥
  if (promise2 === x) {
   return reject(new TypeError('大哥自己等待自己,遊戲結束~'));
  }
  // x 是函式或者物件
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let called; // 防止成功後在呼叫失敗
    try {
      let then = x.then; // 取 x 的 then 方法
      if (typeof then === 'function') {
        // 如果 then 是函式就認為它是 promise
        then.call(x, y => {
          if (called) {
            return;
          }
          called = true;
          // y 可能還是一個 Promsie
          // 遞迴呼叫
          resolvePromise(promise2, y, resolve, reject);
        }, error => {
          if (called) {
            return;
          }
          called = true;
          reject(error);
        });
      } else {
        resolve(x);
      }
    } catch(e) {
      if (called) {
        return;
      }
      called = true;
      reject(e);
    }
  } else { // x 是普通值
    // 呼叫 promise2 中的 resolve 扔出x
    resolve(x);
  }
}

class Promise {
  // 建構函式
  constructor(executor) {
    // 預設狀態
    this.status = 'pending';
    // 將執行器中 resolve(value)、rejecet(reason) 的值存一下
    this.value = undefined;
    this.reason = undefined;
    // 儲存是要解決執行器裡面 非同步呼叫 resolve() 、 reject();
    // 儲存成功回撥
    this.onResolvedCallBacks = [];
    // 儲存失敗回撥
    this.onRejectedCallBacks = [];

    // 呼叫 resolve 後不能再呼叫 reject
    // 狀態只有在 pending 的時候允許改變
    let resolve = (value) => {
      if (this.status === 'pending') {
        // 更新 status
        this.status = 'resolved';
        this.value = value;
        // 讓所有成功的函式執行
        this.onResolvedCallBacks.forEach(fn => fn());
      }
    }
    let reject = (reason) => {
      if (this.status === 'pending') {
        // 更新 status
        this.status = 'rejected';
        this.reason = reason;
        // 讓所有失敗的函式執行
        this.onRejectedCallBacks.forEach(fn => fn());
      }
    }
    // 將函式傳遞出去 並做異常判斷
    try {
      // 呼叫執行器
      executor(resolve, reject);
    } catch(e) {
      reject(e);
    }
  }
  // then 方法
  then(onFulFilled, onRejected) {
    // then 可以預設不穿引數,值的穿透
    onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : y => y;
    onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error };
    // promise then鏈式呼叫 返回的是一個新的 promise 例項
    let promise2;
    if (this.status === 'resolved') {
      // Promise new 的時候執行器立馬執行
      promise2 = new Promise((resolve, reject) => {
        // 新增setTimeout then 方法是非同步呼叫
        setTimeout(() => {
          // excutor 執行的時候會有非同步的情況,需要給每個 then 方法新增try catch
          try {
            // 傳遞 value
            // x 是第一個 then 呼叫後的返回值
            let x = onFulFilled(this.value);
            // x 是否為 promise 如果是 promise 取其結果傳遞給promise2
            // x 是普通值 直接專遞給 promise2
            // promise2 是鏈式呼叫中第2個 then 的 Promise 物件
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) {
            reject(e);
          }
        }, 0);
      });
    }
    if (this.status === 'rejected') {
      promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          try {
            // 傳遞失敗原因
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      });
    }
    // 當前既沒有完成,也沒有失敗
    if (this.status === 'pending') {
      promise2 = new Promise((resolve, reject) => {
        this.onResolvedCallBacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulFilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallBacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      });
    }
    return promise2;
  }
  // catch 方法
  catch(onRejected) {
    // 將當前例項的 then 扔出去
    return this.then(null, onRejected);
  }
}

// Promise 類上的方法
Promise.resolve = function (val) { 
  return new Promise((resolve, reject) => resolve(val));
}
Promise.reject = function (val) {
  return new Promise((resolve, reject) => reject(val));
}
// 有一個失敗就失敗了。
Promise.all = function(promises) {
  return new Promise((resolve, reject) => {
    let arr = [];
    let i = 0; // 監控是否全部成功。
    function processData(index, data) {
      arr[index] = data;
      i++;
      if (i === promises.length) {
        resolve(arr);
      }
    }
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(data => {
        processData(i, data);
      }, reject);
    }
  });
};
// 誰快就用誰的
Promise.race = function(promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(resolve, reject);
    }
  });
}


// promise 語法糖
// 外界呼叫可以不用巢狀了。
Promise.deferred = Promise.defer = function() { 
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  }); 
  return dfd;
}

module.exports = Promise;
複製程式碼

相關文章