異步和promise

引言

16年時在公司分享過一次promise,猶記得當時是第一次分享,還蠻緊張的,當時分享的主要是promise的使用和基本原理,後來又給無線部門同窗分享了一次。
如今回顧想一想,其實講的不是很完美,由於我當時的實現方式相似於簡化版q庫的實現,考慮的也不全面,也沒有徹底遵循promise/a+規範。通過這麼長一段時間的學習和積累,闡述一下本身新的理解。vue

Promise出現的原由

在沒有promise之前,多個有依賴的異步操做通常寫出來會出現嵌套,所謂的回調地域,這種寫法在須要協做的項目中不方便維護,異步操做也不能直接捕獲異常,須要回調中進行處理,缺點挺多的,而後就開始漫長的優化,出現了q, bluebird,jq中的defer等這些庫,後來ES6標準實現了Promise,可是其鏈式寫法仍是不美觀,爲了代碼更優雅,能夠視覺上同步命令式的書寫代碼有了TJ大神的co再結合generator彷佛完美了,可是爲了優雅還要額外引入co庫,成本有點大,後來ES7標準乾脆直接實現了,就是所謂的async和await語法糖node

Promise定義

如今開始切入正題,什麼是Promise呢? 簡而言之promise表明承諾,專業術語就是表明一個異步操做的最終結果。
代碼層面來看的話Promise是一個類,能夠用來建立實例,每一個實例內部封裝一些方法,且維護了一些狀態和值,經過使用這些狀態、值和方法來將現實流程中的承諾具體代碼化表示。ios

Promise使用

promise主要提供了then,catch,all,race,resolve,reject這幾個方法,關於這幾個方法怎麼使用不在贅述,由於佔據文章篇幅長,且不少其它blog重複描述過。推薦阮一峯es6入門中相關api用法解釋,詳細且全面。
關於具體應用的話,因爲在工做中項目基於vue技術棧,因此結合axios時會使用到promise來操做異步,還有就是m站基於pwa,其中Service worker聲明週期事件處理中會涉及promise,還有一些就是平時寫node工具的時候會用到,用promise封裝異步api操做回調,從而將異步api回調邏輯直接放到then方法中進行處理。git

Promise的實現

基於Promise/a+規範實現的代碼能互相統一,雖然代碼形式會有不一樣,但原理都差很少。
首先Promise構造函數中須要有一些狀態和方法,由於執行實例then邏輯的時候須要這些維護好的狀態和值,其中着重提醒的就是promise的狀態機是單向的,且狀態單向不可逆。
狀態轉變只能是 pending -> fulfilled 或者 pending -> rejected。es6

//構造函數初始化邏輯
     let that = this; //緩存this
    //默認狀態爲pending
    that.status = 'pending';
    //此變量裏放着此promise的結果
    that.value = undefined;
    //存放的着全部成功的回調函數
    that.onResolvedCallbacks = [];
    //存放着全部的失敗的回調函數
    that.onRejectedCallbacks = [];

其中內部resolve和reject邏輯以下,更改狀態機狀態,觸發承諾邏輯執行github

function resolve(value) {
        //更改狀態 執行then註冊的成功回調邏輯
        if (that.status == 'pending') {
            //解決resolve 新Promise這種狀況
            if(value!=null &&value.then&&typeof value.then == 'function'){
              return value.then(resolve,reject);
            }
            that.status = 'fulfilled';
            that.value = value;
            that.onResolvedCallbacks.forEach(item=>item(that.value));
        }
    }
     function reject(reason) {
            //更改狀態 執行then註冊的失敗回調邏輯或者catch中註冊的失敗邏輯
            if (that.status == 'pending') {
                that.status = 'rejected';
                that.value = reason;
                that.onRejectedCallbacks.forEach(item=>item(that.value));
            }
        }

上面已經介紹了大體初始化邏輯了,下面着重介紹使用頻率最高的then方法,簡潔版實現以下所示axios

PPromise.prototype.then = function (onFulfilled, onReject) {
    //成功和失敗的邏輯沒有傳遞 會進行值的穿透 傳遞給下一個then方法
     onFulfilled = isFunction(onFulfilled) ?onFulfilled:val =>val;
     onReject = isFunction(onReject) ?onReject:reason => {throw reason;}
     let self = this,promise2;
     switch (self.status){
         case 'fulfilled':
             promise2 = new Promise((resolve,reject) =>{
                 let x = onFulfilled(self.value);
                 if(x instanceof Promise){
                    //遞歸執行then邏輯 直到內部then執行,外部promise2被resolve
                     x.then(resolve,reject)
                 }else{
                     resolve(x);
                 }
             });
             break
         case 'rejected':
             promise2 = new Promise((resolve,reject) =>{
                 let x = onReject(self.value);
                 if(x instanceof Promise){
                     x.then(resolve,reject)
                 }else{
                     resolve(x);
                 }
             })
             break
         case 'pending':
             promise2 = new Promise((resolve,reject) =>{
                 self.onResolvedCallbacks.push(function(){
                     let x = onFulfilled(self.value);
                     if(x instanceof Promise){
                         x.then(resolve,reject)
                     }else{
                         resolve(x);
                     }
                 });
                 self.onRejectedCallbacks.push(function(){
                     let x = onReject(self.value);
                     if(x instanceof Promise){
                         x.then(resolve,reject)
                     }else{
                         resolve(x);
                     }
                 });
             });
     }
     return promise2;
 }

all方法實現segmentfault

function sentry(times,cb){
  let result = [],count=0;
  return function(i,data){
    result[i] = data;
    if(++count==times){
      cb(result);
    }
  }
}
Promise.all = function(promises){
 return new Promise((resolve,reject) => {
   //利用閉包機制,目的是爲了判斷promises是否都執行完
   let done = sentry(promises.length,resolve);
   for(let i=0;i<promises.length;i++){
     promises[i].then(data =>{
       done(i,data);
     },reject);
   }
 });
}

resolve實現api

Promise.resolve = function(value){
  return new Promise(function(resolve){
    resolve(value);
  });
}

race實現promise

Promise.race = function(promises){
  return new Promise((resolve,reject) =>{
    for(let i=0;i<promises.length;i++){
      promises[i].then(resolve,reject);
    }
  });
}

本身實現的promise源碼

異步優雅寫法

異步操做通過promisify轉化成promise,在結合async實現優雅的寫法

let Promise = require('bluebird');
let readFile = Promise.promisify(require('fs').readFile);
async function read() {
  let a = await readFile('./1.txt','utf8');
  let b = await readFile('./2.txt','utf8');
  let c = await readFile('./3.txt','utf8');
  console.log(c);
  return 'ok';
}

read().then(data => {
  console.log(data);
});

總結

任何事物都不是一蹴而就的,都有一個發展過程才逐步變得完美,將本身的學習坐下記錄,並加一些我的思考,若是對於本文有任何疑問或錯誤,歡迎斧正交流。

參考連接
https://promisesaplus.com/
https://segmentfault.com/a/11...

相關文章
相關標籤/搜索