最近新型冠狀病毒嚴重,在家也沒啥事,寫寫文章,Promise 前端開發或多或少都有了解或使用到,我也常常用到。 但願閱讀本章能幫助瞭解Promise究竟是怎麼實現的,或多或少能幫你^_^
在javascript開發過程當中,代碼是單線程執行的,同步操做,彼此之間不會等待,這能夠說是它的優點,可是也有它的弊端,如一些網絡操做,瀏覽器事件,文件等操做等,都必須異步執行,針對這些狀況,起初的操做都是使用回調函數實現。javascript
實現方式以下(僞代碼):html
function One(callback) { if (success) { callback(err, result); } else { callback(err, null); } } One(function (err, result) { //執行完One函數內的內容,成功的結果回調回來向下執行 })
上述代碼只是一層級回調,若是代碼複雜後,會出現多層級的回調,代碼可讀性也會不好,那有沒有一種方式,不用考慮裏面的內容,直接根據結果成功仍是失敗執行下面的代碼呢?有的,Promise(承諾),在ES6中對Promise進行了統一的規範前端
Promise可能你們都不陌生,由於Promise規範已經出來好一段時間了,同時Promise也已經歸入了ES6,並且高版本的chrome、firefox瀏覽器都已經原生實現了Promise,只不過和現現在流行的類Promise類庫相比少些API。java
Promise規範以下:git
then
方法(能夠說,then就是promise的核心),並且then必須返回一個promise,同一個promise的then能夠調用屢次,而且回調的執行順序跟它們被定義時的順序一致由以上規範就容易就能實現這個類的大體結構github
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Promise</title> </head> <body> <script type="text/javascript"> class Promise { callbacks = []; constructor(fn) { fn(this.resolve.bind(this)); } then(onFulfilled) { this.callbacks.push(onFulfilled); } resolve(value) { this.callbacks.forEach(fn => fn(value)); } } //Promise應用 let p = new Promise(resolve => { setTimeout(() => { resolve('測試'); }, 2000); }) p.then((tip)=>{ console.log('tip1',tip) // tip1,測試 }) p.then((tip)=>{ console.log('tip2',tip) // tip2,測試 }) </script> </body> </html>
這個簡單版本大體邏輯是:
實例化Promise時,其類構造函數初始化執行了回調函數,並將綁定了當前實例的resolve方法做爲參數回傳給回到函數。接着調動Promise對象的then方法, 註冊異步操做完成後的回調函數。 當異步操做完成時,調用resolve方法, 該方法執行then方法註冊的回調函數。
這裏then 方法註冊完成時的回到是一個數組, then方法能夠屢次調用。註冊的函數會在異步操做完成後根據添加的順序依次執行。chrome
相信仔細的人應該能夠看出來,then
方法應該可以鏈式調用,可是上面的最基礎簡單的版本顯然沒法支持鏈式調用。想讓then
方法支持鏈式調用,其實也是很簡單的(若是寫過jQuery插件的同窗應該熟悉):數組
// 在上方代碼添加 then(onFulfilled) { this.callbacks.push(onFulfilled); return this; //看這裏 } // 修改其調用方式 p.then((tip)=>{ console.log('tip1',tip) // tip1,測試 }).then((tip)=>{ console.log('tip2',tip) // tip2,測試 })
首先咱們吧上方代碼的栗子中 setTimeout 去掉,在執行一下promise
//Promise應用 let p = new Promise(resolve => { console.log('同步執行1'); resolve('同步執行2'); }).then(tip => { console.log('then1', tip); }).then(tip => { console.log('then2', tip); });
發現只打印了 同步執行1
這顯然是不容許的,Promises/A+
規範明確要求回調須要經過異步方式執行,用以保證一致可靠的執行順序。所以咱們要加入一些處理,保證在resolve
執行以前,then
方法已經註冊完全部的回調。咱們能夠這樣改造下resolve
函數瀏覽器
// 修改上方代碼 resolve(value) { //看這裏 setTimeout(() => { this.callbacks.forEach(fn => fn(value)); }); } //經過`setTimeout`機制,將`resolve`中執行回調的邏輯放置到`JS`任務隊列末尾,以保證在`resolve`執行時,`then`方法的回調函數已經註冊完成.
爲了解決上面提到的問題, 咱們須要加入狀態機制, 也就是你們熟知的pending, fulfilled, rejected。
Promises/A+ 規範中明確規定了, pending 能夠轉化爲fulfilled或rejected而且只能轉化一次。 也就是說若是pending轉爲fulfiled就不能再轉化到rejected。 而且fulfilled 和 rejected狀態只能pending轉化而來, 二者之間不能互相轉換。
// 修改以下 class Promise { callbacks = []; state = 'pending';//增長狀態 value = null;//保存結果 constructor(fn) { fn(this.resolve.bind(this)); } then(onFulfilled) { if (this.state === 'pending') { //在resolve以前,跟以前邏輯同樣,添加到callbacks中 this.callbacks.push(onFulfilled); } else { //在resolve以後,直接執行回調,返回結果了 onFulfilled(this.value); } return this; } resolve(value) { this.state = 'fulfilled';//改變狀態 this.value = value;//保存結果 this.callbacks.forEach(fn => fn(value)); } }
resolve 執行時, 會將狀態設置爲 fulfilled , 並把 value 的值存起來, 在此以後調用 then 添加的新回調都會當即執行, 直接返回保存的value值
有同窗發現增長了狀態的後代碼把setTimeout去掉了,緣由是:resolve
執行時,會將狀態設置爲fulfilled
,在此以後調用then
添加的新回調,都會當即執行
那麼這裏問題又來了,若是用戶再then函數裏面註冊的仍然是一個Promise
,該如何解決?好比下面
p() .then(()=>{ // 這裏返回Promise return new Promise(function (resolve) { resolve(resolve); }); }) .then(function (res) { // res拿到上一個Promise.resolve的值 });
真正的鏈式調用
真正的鏈式Promise是指在當前Promise達到fulfilled狀態後, 即開始進行下一個Promise(後鄰Promise)。 那麼咱們如何銜接當前Promise和後鄰Promise呢,這個是重點,修改較多,我附一段完整的代碼
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Promise</title> </head> <body> <script type="text/javascript"> class Promise { callbacks = []; state = 'pending';//增長狀態 value = null;//保存結果 constructor(fn) { fn(this.resolve.bind(this)); } then(onFulfilled) { return new Promise(resolve => { this.handle({ onFulfilled: onFulfilled || null, resolve: resolve }); }); } handle(callback) { if (this.state === 'pending') { this.callbacks.push(callback); return false; } //若是then中沒有傳遞任何東西 if (!callback.onFulfilled) { callback.resolve(this.value); return false; } var ret = callback.onFulfilled(this.value); callback.resolve(ret); } resolve(value) { if (value && (typeof value === 'object' || typeof value === 'function')) { var then = value.then; if (typeof then === 'function') { then.call(value, this.resolve.bind(this)); return; } } this.state = 'fulfilled';//改變狀態 this.value = value;//保存結果 this.callbacks.forEach(fn => fn(value)); } } // then中返回Promise let p = new Promise(resolve => { console.log('同步執行1'); resolve('同步執行2'); }).then(tip => { console.log(tip); return new Promise((resolve)=>{ resolve('同步執行3') }) }).then(tip => { console.log(tip); }); </script> </body> </html>
then
方法中,建立並返回了新的Promise
實例,這是串行Promise
的基礎,而且支持鏈式調用。handle
方法是promise
內部的方法。then
方法傳入的形參onFulfilled
以及建立新Promise
實例時傳入的resolve
均被push
到當前promise
的callbacks
隊列中,這是銜接當前promise
和後鄰promise
的關鍵所在(這裏必定要好好的分析下handle的做用)。在異常操做失敗時,標記其狀態爲rejected, 並執行註冊的失敗回調
<!--完整代碼--> <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Promise</title> </head> <body> <script type="text/javascript"> class Promise { callbacks = []; state = 'pending';//增長狀態 value = null;//保存結果 constructor(fn) { fn(this.resolve.bind(this), this.reject.bind(this)); } then(onFulfilled, onRejected) { return new Promise((resolve, reject) => { this.handle({ onFulfilled: onFulfilled || null, onRejected: onRejected || null, resolve: resolve, reject: reject }); }); } handle(callback) { if (this.state === 'pending') { this.callbacks.push(callback); return; } let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected; if (!cb) {//若是then中沒有傳遞任何東西 cb = this.state === 'fulfilled' ? callback.resolve : callback.reject; cb(this.value); return; } // 這裏處理,若是在執行成功回調、失敗回調時代碼出錯怎麼辦,對於相似異常, 處理也很簡單, 可使用try-catch捕獲錯誤, 而後將相應的promise狀態設置爲rejected狀態 let ret; try { ret = cb(this.value); cb = this.state === 'fulfilled' ? callback.resolve : callback.reject; } catch (error) { ret = error; cb = callback.reject } finally { cb(ret); } } resolve(value) { if (value && (typeof value === 'object' || typeof value === 'function')) { var then = value.then; if (typeof then === 'function') { then.call(value, this.resolve.bind(this), this.reject.bind(this)); return; } } this.state = 'fulfilled';//改變狀態 this.value = value;//保存結果 this.callbacks.forEach(callback => this.handle(callback)); } reject(error) { this.state = 'rejected'; this.value = error; this.callbacks.forEach(callback => this.handle(callback)); } } //Promise應用 let p = new Promise(resolve => { console.log('同步執行1'); resolve('同步執行2'); }).then(tip => { return new Promise((resolve,reject)=>{ // 作個隨機數控制resolve或者reject的調用 if(parseInt(Math.random()*10) > 4){ resolve(tip+' 成功') }else{ reject(tip+' 失敗') } }) }).then(result => { console.log(result); }, error => { console.log(error); }); </script> </body> </html>
promise 裏面的 then 函數僅僅是註冊了後續須要執行的回調函數,同時返回一個新的Promise對象,以延續鏈式調用,真正的邏輯是在handle裏面
對於內部 pending 、fulfilled 和 rejected 的狀態轉變,經過 handler 觸發 resolve 和 reject 方法,而後更改state狀態值
MPromise
分層解析 Promise 的實現原理
Promise 原理解析 (漸進實例詳解)