16年時在公司分享過一次promise,猶記得當時是第一次分享,還蠻緊張的,當時分享的主要是promise的使用和基本原理,後來又給無線部門同窗分享了一次。
如今回顧想一想,其實講的不是很完美,由於我當時的實現方式相似於簡化版q庫的實現,考慮的也不全面,也沒有徹底遵循promise/a+規範。通過這麼長一段時間的學習和積累,闡述一下本身新的理解。vue
在沒有promise之前,多個有依賴的異步操做通常寫出來會出現嵌套,所謂的回調地域,這種寫法在須要協做的項目中不方便維護,異步操做也不能直接捕獲異常,須要回調中進行處理,缺點挺多的,而後就開始漫長的優化,出現了q, bluebird,jq中的defer等這些庫,後來ES6標準實現了Promise,可是其鏈式寫法仍是不美觀,爲了代碼更優雅,能夠視覺上同步命令式的書寫代碼有了TJ大神的co再結合generator彷佛完美了,可是爲了優雅還要額外引入co庫,成本有點大,後來ES7標準乾脆直接實現了,就是所謂的async和await語法糖node
如今開始切入正題,什麼是Promise呢? 簡而言之promise表明承諾,專業術語就是表明一個異步操做的最終結果。
代碼層面來看的話Promise是一個類,能夠用來建立實例,每一個實例內部封裝一些方法,且維護了一些狀態和值,經過使用這些狀態、值和方法來將現實流程中的承諾具體代碼化表示。ios
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/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...