淺談promise

hiSky發表於2018-05-20

淺談promise

最近要開始深入學習並鞏固前端知識,此文僅以用來記錄自己的學習筆記,如有錯誤請指正. 

首先說說我對promise的瞭解,

供參考文件:
1. promiseA+

2. 阮一峰日誌

1. promise的引出

js中所有程式碼都是單執行緒執行的,導致JavaScript的所有網路操作,瀏覽器事件,都必須是非同步執行

當達到某個條件時 執行callback

function after(times, callback) {  
    return function () {   
     if (--times === 0) {      callback();    } 
     }}
let eat = after(3, function () {  console.log('ok');});
eat();
eat();


複製程式碼

到達規定的時間後 執行callback
function callback() {
    console.log('Done');
}
console.log('before setTimeout()');
setTimeout(callback, 1000); // 1秒鐘後呼叫callback函式
console.log('after setTimeout()');複製程式碼

  • 或者頻繁回撥

function getData() {    $.ajax({
        url: 'data.json',
        type: 'get',
        success: function (data) {
            review(data);
        }
    });
}複製程式碼


這樣寫很正常,但是會巢狀回撥,而且不太美觀.

2. promise 的引出解決了以上問題

  • let fs = require('fs');
    function after(times,callback) {  
    let arr = [];  
    return function (d) {    
    arr.push(d);    if(arr.length === times) callback(arr);  
    }}
    let out = after(2, function (data) {  console.log(data);});
    fs.readFile('a.txt', 'utf8', function (err, data) {  out(data);})複製程式碼


  • 鏈式呼叫

    p.then((data)=> {
      console.log(1)
    },(err)=> {
       console.log(2)
    }).then((data)=>{
        console.log('成功')
    },(err)=>{
        console.log('失敗')
    })複製程式碼

3. promise的三個狀態

  1. resolved(成功態)
  2. rejected(失敗態)
  3. pending (未完成)
    狀態一旦從pending 狀態 執行為resolved或rejected ,狀態不會轉換, 誰先執行完 ,誰結束這個promise

簡單實現一下promise

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('迴圈引用'));
  }
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let called;
    try { // 防止取then是出現異常 Object.defineProperty
      let then = x.then; // 取x的then方法 {then:{}}
      if (typeof then === 'function') { // 如果then是函式我就認為它是promise
        // call 第一個引數是this ,後面的是成功的回撥和失敗的回撥
        then.call(x, y => { // 如果y是promise就繼續遞迴解析promise
          if (called) return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, r => { // 只要失敗了就失敗了
          if (called) return;
          called = true;
          reject(r);
        });
      } else { // then是一個普通物件,就直接成功即可1
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else { // x = 123
    resolve(x); // x就是一個普通值
  }
}
複製程式碼


class Promise {
  constructor(executor) {
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = (data) => {
      if (this.status === 'pending') {
        this.value = data;
        this.status = 'resolved';
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    }
    let reject = (reason) => {
      if (this.status === 'pending') {
        this.reason = reason;
        this.status = 'rejected';
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    }
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulFilled, onRejected) {
    onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : y => y;
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
    let promise2;
    if (this.status === 'resolved') {
      promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          try {
            let x = onFulFilled(this.value);
            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; // 呼叫then後返回一個新的promise
  }
  // catch接收的引數 只用錯誤
  catch(onRejected) {
    // catch就是then的沒有成功的簡寫
    return this.then(null, onRejected);
  }
}
Promise.resolve = function (val) {
  return new Promise((resolve, reject) => resolve(val))
}
Promise.reject = function (val) {
  return new Promise((resolve, reject) => reject(val));
}
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(resolve, reject);
    }
  });
}
Promise.all = function (promises) {
  return new Promise((resolve,reject)=>{
    let arr = [];
    let i = 0; // i的目的是為了保證獲取全部成功,來設定的索引
    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.deferred = Promise.defer = function () {
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  })
  return dfd;
}
module.exports = Promise;Promise.resolve() 返回一個成功的promise
複製程式碼

Promise.reject() 返回一個失敗的promise

promise.race處理多請求只取最快的 

promise.all 併發呼叫,當所有Promise 物件都變為完成態或失敗態時,回撥將被執行。