promise原理就是這麼簡單

異步編程的幾種形式:

回調函數形式:

function f1(callback){
    callback();
}
function f2(callback){
    callback();
}
function f3(callback){
    callback();
}
f1(f2(f3))
複製代碼

這種方式實現異步編程優勢是思路清晰,以串行的思考方式進行編程,缺點是造成回調地獄,過多的回調嵌套使得代碼變得難以理解拆分和維護。es6

發佈訂閱模式

let dep = {
  list: [],
  on: function (fn) {
    list.push(fn);
  },
  emit: function () {
    this.list.forEach(event => {
      typeof event === 'function' ? event() : null;
    })
  }
};
複製代碼

上面就是簡易版的發佈訂閱模式:發佈者存在一個數組list用於登記訂閱者即異步執行的函數,等到必定條件下執行emit,訂閱的異步函數都會執行。這就比如發佈者售樓中心的擁有一個登記冊,裏面登記須要買房的全部訂閱者,有的訂閱者登記的是電話通知,有的訂閱者登記的郵件通知,等到樓盤信息變化時會主動給每一個訂閱者執行相應的操做(執行對應的函數)編程

promise

promise的核心原理其實就是發佈訂閱模式,經過兩個隊列來緩存成功的回調(onResolve)和失敗的回調(onReject)。若是還不熟悉promise用法的朋友,請參考Es6入門之promise對象,下面來分析promise的特色。數組

promise的特色:

  1. new Promise時須要傳遞一個executor執行器,執行器會馬上執行
  2. 執行器中傳遞了兩個參數:resolve成功的函數、reject失敗的函數,他們調用時能夠接受任何值的參數value
  3. promise狀態只能從pending態轉onfulfilled,onrejected到resolved或者rejected,而後執行相應緩存隊列中的任務
  4. promise實例,每一個實例都有一個then方法,這個方法傳遞兩個參數,一個是成功回調onfulfilled,另外一個是失敗回調onrejected
  5. promise實例調用then時,若是狀態resolved,會讓onfulfilled執行而且把成功的內容看成參數傳遞到函數中
  6. promise中能夠同一個實例then屢次,若是狀態是pengding 須要將函數存放起來 等待狀態肯定後 在依次將對應的函數執行 (發佈訂閱)

promise基礎版實現

下面針對這些特色來實現promise:promise

new Promise時須要傳遞一個executor執行器,執行器會馬上執行;執行器中傳遞了兩個參數:resolve成功的函數、reject失敗的函數,他們調用時能夠接受任何值的參數value
function Promise (executor){
    function resolve(value){}
    function reject(value){}
    try{
        executor(resolve,reject);
    }catch(e){
        reject(e);
    }
}
var promise = new Promise((resolve,reject)=>{
    console.log('start');
})
複製代碼
promise狀態只能從pending態轉onfulfilled,onrejected到resolved或者rejected
function Promise (executor) {
  var self = this;//resolve和reject中的this指向不是promise實例,須要用self緩存
  self.state = 'padding';
  self.value = '';//緩存成功回調onfulfilled的參數
  self.reson = '';//緩存失敗回調onrejected的參數
  self.onResolved = []; // 專門存放成功的回調onfulfilled的集合
  self.onRejected = []; // 專門存放失敗的回調onrejected的集合
  function resolve (value) {
    if(self.state==='padding'){
      self.state==='resolved';
      self.value=value;
      self.onResolved.forEach(fn=>fn())
    }
  }
  function reject (reason) {
    self.state = 'rejected';
    self.value = reason;
    self.onRejected.forEach(fn=>fn())
  }
  try{
    executor(resolve,reject)
  }catch(e){
    reject(e)
  }
}
複製代碼
promise實例,每一個實例都有一個then方法,這個方法傳遞兩個參數,一個是成功回調onfulfilled,另外一個是失敗回調onrejected;
promise實例調用then時,若是狀態resolved,會讓onfulfilled執行而且把成功的內容看成參數傳遞到函數中;
promise中能夠同一個實例then屢次,若是狀態是pengding 須要將函數存放起來 等待狀態肯定後 在依次將對應的函數執行(發佈訂閱)
Promise.prototype.then=function (onfulfilled,onrejected) {
  var self=this;
  if(this.state==='resolved'){
    onfulfilled(self.value)
  }
  if(this.state==='rejected'){
    onrejected(self.value)
  }
  if(this.state==='padding'){
    this.onResolved.push(function () {
      onfulfilled(self.value)
    })
  }
}
複製代碼

then方法的特色:

以上只是實現了promise的基本功能,可是還缺乏then的鏈式調用,then函數參數onfulfilled,onrejected缺省處理,鏈式調用返回值多種狀況的處理,下面分析then方法的特色:緩存

  1. 由於promise狀態肯定後就是不能更改,因此每次promise執行then後都會返回一個新的promise而不是this,那麼狀態永遠爲resolve或jeject,將存在異步調用
  2. onfulfilled或onrejected是一個可選參數,須要作沒有傳遞時的處理
  3. 若是then中onfulfilled或onrejected返回的是一個普通值的話會把這個結果傳遞下一次then中的成功回調
  4. 若是then中onfulfilled或onrejected出現異常,會走下一個then的失敗回調,將err傳遞到失敗回調中
  5. 若是失敗後還能夠成功,若是返回undefined,會把undefined傳遞給下一次
  6. 若是then方法返回的是一個promise,那麼會等待這個promise執行完決定返回的是成功仍是失敗

promise優化版實現

由於promise狀態肯定後就是不能更改,因此每次promise執行then後都會返回一個新的promise而不是this,那麼狀態永遠爲resolve或jeject,將存在異步調用;onfulfilled或onrejected是一個可選參數,須要作沒有傳遞時的處理
Promise.prototype.then=function (onfulfilled,onrejected) {
    onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;//onfulfilled缺省處理
    onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};//onrejected缺省處理
  var self=this,promise2=new Promise(function(resolve,reject){//返回一個promise
    if(this.state==='resolved'){
    try{
        onfulfilled(self.value);//裏面會執行resolve    
    }catch(e){
        reject(e);
    }
  }
  if(this.state==='rejected'){
    try{
        onrejected(self.value);
    }catch(e){
        reject(e);
    }
  }
  if(this.state==='padding'){//將執行過程緩存
   self.onResolved.push(function () {
        try{
            onfulfilled(self.value);
        }catch(e){
           reject(e)
        }
    });
    self.onRejected.push(function () {
        try{
            onrejected(self.value);
        }catch(e){
            reject(e)
        }
    })
  }  
  })
  return promise2;
}
複製代碼
若是then中onfulfilled或onrejected返回的是一個普通值的話會把這個結果傳遞下一次then中的成功回調;
若是then中onfulfilled或onrejected出現異常,會走下一個then的失敗回調,將err傳遞到失敗回調中;
若是失敗後還能夠成功,若是返回undefined,會把undefined傳遞給下一次;
若是then方法返回的是一個promise,那麼會等待這個promise執行完決定返回的是成功仍是失敗,因此須要對onfulfilled或onrejected的返回值進行處理。
Promise.prototype.then=function (onfulfilled,onrejected) {
    onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;
    onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};
  var self=this,
    res=null,//用來緩存onfulfilled或onrejected的返回值
    promise2=new Promise(function(resolve,reject){
    if(this.state==='resolved'){
    try{
        res = onfulfilled(self.value);//獲得onfulfilled的返回值
        resolvePromise(promise2,res,resolve,reject);//返回值的處理函數
    }catch(e){
        reject(e);
    }
  }
  if(this.state==='rejected'){
    try{
        res = onrejected(self.value);//獲得onrejected的返回值
        resolvePromise(promise2,res,resolve,reject);
    }catch(e){
        reject(e);
    }
  }
  if(this.state==='padding'){
   self.onResolved.push(function () {
        try{
            res = onfulfilled(self.value);
            resolvePromise(promise2,res,resolve,reject);
        }catch(e){
           reject(e)
        }
    });
    self.onRejected.push(function () {
        try{
            res = onrejected(self.value);
            resolvePromise(promise2,res,resolve,reject);
        }catch(e){
            reject(e)
        }
    })
  }  
  })
  return promise2;
}
function resolvePromise(promise,res,resolve,reject) {
  if(promise===res){//防止循環引用
    return reject(new TypeError('循環引用'))
  }
  let called;//防止重複執行
  if(res!==null&&(typeof res==='function'||typeof res ==='object')){
    try {//防止promise執行報錯
      let then=res.then;//判斷是否promise就判斷是否存在then方法
      if(typeof then ==='function'){//若是返回的是promise,只須要在返回的promise的then方法中下一步須要執行的函數
        then.call(res,(res2)=>{
          if (called) return;
          called = true;
          resolvePromise(promise,res2,resolve,reject);//若是是promise繼續遞歸執行,直到不是promise,依次執行外層的resolve,讓promise狀態改變
        },)
      }else{//若是不是promise,有多是undefine、onfulfilled或onrejected的返回的普通值,就直接將這個值返回,將外層的promise狀態改變
        if (called) return;
        called = true;
        resolve(then)
      }
    }catch(e){
      if (called) return;
      called = true;
      reject(e)
    }
  }else{
    resolve(res)
  }
};
複製代碼
promise.then屬於異步微任務,then中的方法,必須等到全部的同步任務執行完才執行,宏任務和微任務能夠查看EventLoop其實如此簡單
console.log(1);
var promise=new Promise(function(resolve,reject){
    resolve('a');
})
promise.then(function(value){
    console.log(value)
})
console.log(2);
// 1 2 'a'
複製代碼
//因爲promise有內部的機制實現微任務,因此這裏使用setTimeout代替
Promise.prototype.then=function (onfulfilled,onrejected) {
    onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;
    onrejected = typeof onrejected === 'function' ? onrejected : err => {throw err;};
  var self=this,
    res=null,
    promise2=new Promise(function(resolve,reject){
    if(this.state==='resolved'){
        setTimeout(()=>{
            try{
                res = onfulfilled(self.value);
                resolvePromise(promise2,res,resolve,reject);
            }catch(e){
                reject(e);
            }
        })
    }
  if(this.state==='rejected'){
        setTimeout(()=>{
            try{
                res = onrejected(self.value);
                resolvePromise(promise2,res,resolve,reject);
            }catch(e){
                reject(e);
            }  
        })
    }
  if(this.state==='padding'){
    self.onResolved.push(function () {
        setTimeout(()=>{
            try{
                res = onfulfilled(self.value);
                resolvePromise(promise2,res,resolve,reject);
            }catch(e){
                reject(e);
            }
        })
    });
    self.onRejected.push(function () {
        setTimeout(()=>{
            try{
                res = onrejected(self.value);
                resolvePromise(promise2,res,resolve,reject);
            }catch(e){
                reject(e);
            }  
        })
    })
  }  
  })
  return promise2;
}
複製代碼

promise.catch會捕獲到沒有捕獲的異常;

Promise.resolve會返回一個狀態位rsolved的promise;

Promise.reject會返回一個狀態位rsolved的promise;

Promise.all會在全部的promise resolved後執行回調,Promise.race只要有一個promise resolved就執行回調。

promise.catch將全部的錯誤在promise實例的下一個then中返回
Promise.prototype.catch = function (onrejected) {
  return this.then(null, onrejected)
};
複製代碼
Promise.reject和Promise.reject
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason)
  })
};
Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    resolve(value);
  })
};
複製代碼
Promise.all和Promise.race
//在每一個promise的回調中添加一個判斷函數processData(就是在當前的promise.then中添加),每一個promise狀態改變後讓index++,直到和promises的個數相等就執行回調
Promise.all=function (promises) {
  return new Promise((resolve,reject)=>{
    let results=[],i=0;
    for(let i=0;i<promises.length;i++){
      let p=promises[i];
      p.then((data)=>{
        processData(i,data)
      },reject)
    }
    function processData (index,data) {
      results[index]=data;
      if(++i==promises.length){
        resolve(results)
      }
    }
  })
};
//在每一個promise的回調中添加一個resolve(就是在當前的promise.then中添加),有一個狀態改變,就讓race的狀態改變
Promise.race=function (promises) {
  return new promises((resolve,reject)=>{
    for(let i=0;i<promises.length;i++){
      let p=promises[i];
      p.then(resolve,reject)
    }
  })
};
複製代碼

generator + co

Generator函數能夠經過yield暫停執行和next恢復執行,因此能夠封裝一個函數來自動執行next函數而使Generator完成異步任務。bash

let fs = require('mz/fs');
function * read() {
  let age = yield fs.readFile('./name.txt','utf8');
  let adress = yield fs.readFile(age,'utf8');
  let r = yield fs.readFile(adress,'utf8');
  return r;
}
function co(it) {//it爲一個generator對象
//返回一個promise而且執行generator對象的next
return new Promise((resolve,reject)=>{
    function next(data) {
        //獲取前一個異步函數的返回值,其必須爲promise
        let { value, done } = it.next(data);  
            if(!done){
            //返回promise的then方法中添加generator的next
            //前一個異步函數執行成功會使返回的promise成resolved
            //而後執行generator的next,遞歸依次類推
            value.then(data=>{  
                next(data)
            }, reject);
        }else{
            resolve(value);
        }
    }
        next();
  })
}
co(read()).then(data=>{
  console.log(data);
},err=>{
  console.log(err);
});
複製代碼

async和awit

async+awit等於generator+co,而co中實現generator自動化是基於Promise,因此awit會使用new Promise形式。異步

結語

若是以上有任何錯誤之處,但願提出並請指正,若是對promise使用還不清楚的朋友,請參考Es6入門之promise對象,本文參考:async

  1. Promise A+規範
  2. Es6入門之promise對象
  3. Promise 源碼
  4. 教你一步步實現promise
相關文章
相關標籤/搜索