您好,登錄后才能下訂單哦!
本篇內容介紹了“分析Promise鏈式調用”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
上一節中,實現了 Promise 的基礎版本:
//極簡的實現+鏈式調用+延遲機制+狀態 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)); } }
但鏈式調用,只是在 then 方法中 return 了 this,使得 Promise 實例可以多次調用 then 方法,但因為是同一個實例,調用再多次 then 也只能返回相同的一個結果,通常我們希望的鏈式調用是這樣的:
//使用Promise function getUserId(url) { return new Promise(function (resolve) { //異步請求 http.get(url, function (id) { resolve(id) }) }) } getUserId('some_url').then(function (id) { //do something return getNameById(id); }).then(function (name) { //do something return getCourseByName(name); }).then(function (course) { //do something return getCourseDetailByCourse(course); }).then(function (courseDetail) { //do something });
每個 then 注冊的 onFulfilled 都返回了不同的結果,層層遞進,很明顯在 then 方法中 return this 不能達到這個效果。引入真正的鏈式調用,then 返回的一定是一個新的Promise實例。
真正的鏈式 Promise 是指在當前 Promise 達到 fulfilled 狀態后,即開始進行下一個 Promise(后鄰 Promise)。那么我們如何銜接當前 Promise 和后鄰 Promise 呢?(這是理解 Promise 的難點,我們會通過動畫演示這個過程)。
先看下實現源碼:
//完整的實現 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; } //如果then中沒有傳遞任何東西 if (!callback.onFulfilled) { callback.resolve(this.value); return; } var ret = callback.onFulfilled(this.value); callback.resolve(ret); } _resolve(value) { this.state = 'fulfilled';//改變狀態 this.value = value;//保存結果 this.callbacks.forEach(callback => this._handle(callback)); } }
由上面的實現,我們可以看到:
then 方法中,創建并返回了新的 Promise 實例,這是串行Promise的基礎,是實現真正鏈式調用的根本。
then 方法傳入的形參 onFulfilled 以及創建新 Promise 實例時傳入的 resolve 放在一起,被push到當前 Promise 的 callbacks 隊列中,這是銜接當前 Promise 和后鄰 Promise 的關鍵所在。
根據規范,onFulfilled 是可以為空的,為空時不調用 onFulfilled。
看下動畫演示:
(Promise 鏈式調用演示動畫)
當第一個 Promise 成功時,resolve 方法將其狀態置為 fulfilled ,并保存 resolve 帶過來的value。然后取出 callbacks 中的對象,執行當前 Promise的 onFulfilled,返回值通過調用第二個 Promise 的 resolve 方法,傳遞給第二個 Promise。動畫演示如下:
(Promise 鏈式調用 fulfilled)
為了真實的看到鏈式調用的過程,我寫一個mockAjax函數,用來模擬異步請求:
/** * 模擬異步請求 * @param {*} url 請求的URL * @param {*} s 指定該請求的耗時,即多久之后請求會返回。單位秒 * @param {*} callback 請求返回后的回調函數 */ const mockAjax = (url, s, callback) => { setTimeout(() => { callback(url + '異步請求耗時' + s + '秒'); }, 1000 * s) }
除此之外,我給 Promise 的源碼加上了日志輸出并增加了構造順序標識,可以清楚的看到構造以及執行過程:
//Demo1 new Promise(resolve => { mockAjax('getUserId', 1, function (result) { resolve(result); }) }).then(result => { console.log(result); })
【 Demo1 的源碼】
執行結果如下:
[Promse-1]:constructor [Promse-1]:then [Promse-2]:constructor [Promse-1]:_handle state= pending [Promse-1]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ] => Promise { callbacks: [], name: 'Promse-2', state: 'pending', value: null } [Promse-1]:_resolve [Promse-1]:_resolve value= getUserId異步請求耗時1秒 [Promse-1]:_handle state= fulfilled getUserId異步請求耗時1秒 [Promse-2]:_resolve [Promse-2]:_resolve value= undefined
通過打印出來的日志,可以看到:
構造 Promise-1 實例,立即執行 mackAjax('getUserId',callback);
調用 Promise-1 的 then 方法,注冊 Promise-1 的 onFulfilled 函數。
then 函數內部構造了一個新的 Promise實例:Promise-2。立即執行 Promise-1 的 _handle方法。
此時 Promise-1 還是pending的狀態。
Promise-1._handle 中就把注冊在 Promise-1 的 onFulfilled 和 Promise-2 的 resolve 保存在 Promise-1 內部的 callbacks。
至此當前線程執行結束。返回的是 Promise-2 的 Promise實例。
1s后,異步請求返回,要改變 Promise-1 的狀態和結果,執行 resolve(result)。
Promise-1 的值被改變,內容為異步請求返回的結果:"getUserId異步請求耗時1s"。
Promise-1 的狀態變成 fulfilled。
Promise-1 的 onFulfilled 被執行,打印出了"getUserId異步請求耗時1秒"。
然后再調用 Promise-2.resolve。
改變 Promise-2 的值和狀態,因為 Promise-1 的 onFulfilled 沒有返回值,所以 Promise-2的值為undefined。
上例中,如果把異步的請求改成同步會是什么的效果?
new Promise(resolve => { resolve('getUserId同步請求'); }).then(result => { console.log(result); }); //打印日志 [Promse-1]:constructor [Promse-1]:_resolve [Promse-1]:_resolve value= getUserId同步請求 [Promse-1]:then [Promse-2]:constructor [Promse-1]:_handle state= fulfilled getUserId同步請求 [Promse-2]:_resolve [Promse-2]:_resolve value= undefined => Promise { callbacks: [], name: 'Promse-2', state: 'fulfilled', value: undefined }
感興趣的可以自己去分析一下。
執行當前 Promise 的 onFulfilled 時,返回值通過調用第二個 Promise 的 resolve 方法,傳遞給第二個 Promise,作為第二個 Promise 的值。于是我們考慮如下Demo:
//Demo2 new Promise(resolve => { mockAjax('getUserId', 1, function (result) { resolve(result); }) }).then(result => { console.log(result); //對result進行第一層加工 let exResult = '前綴:' + result; return exResult; }).then(exResult => { console.log(exResult); });
【 Demo2 的源碼】
我們加了一層 then,來看下執行的結果:
[Promse-1]:constructor [Promse-1]:then [Promse-2]:constructor [Promse-1]:_handle state= pending [Promse-1]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ] [Promse-2]:then [Promse-3]:constructor [Promse-2]:_handle state= pending [Promse-2]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ] => Promise { callbacks: [], name: 'Promse-3', state: 'pending', value: null } [Promse-1]:_resolve [Promse-1]:_resolve value= getUserId異步請求耗時1秒 [Promse-1]:_handle state= fulfilled getUserId異步請求耗時1秒 [Promse-2]:_resolve [Promse-2]:_resolve value= 前綴:getUserId異步請求耗時1秒 [Promse-2]:_handle state= fulfilled 前綴:getUserId異步請求耗時1秒 [Promse-3]:_resolve [Promse-3]:_resolve value= undefined:
鏈式調用可以無限的寫下去,上一級 onFulfilled return 的值,會變成下一級 onFulfilled 的結果。可以參考Demo3:
【 Demo3 的源碼】
我們很容易發現,上述 Demo3 中只有第一個是異步請求,后面都是同步的,我們完全沒有必要這么鏈式的實現。如下一樣能得到我們想要的三個結果: 分別打印出來的值。
//等價于 Demo3 new Promise(resolve => { mockAjax('getUserId', 1, function (result) { resolve(result); }) }).then(result => { console.log(result); //對result進行第一層加工 let exResult = '前綴:' + result; console.log(exResult); let finalResult = exResult + ':后綴'; console.log(finalResult); });
那鏈式調用真正的意義在哪里呢?
剛才演示的都是 onFulfilled 返回值是 value 的情況,如果是一個 Promise 呢?是不是就可以通過 onFulfilled,由使用 Promise 的開發者決定后續 Promise 的狀態。
于是在 _resolve 中增加對前一個 Promise onFulfilled 返回值的判斷:
_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(callback => this._handle(callback)); }
從代碼上看,它是對 resolve 中的值作了一個特殊的判斷,判斷 resolve 的值是否為 Promise實例,如果是 Promise 實例,那么就把當前 Promise 實例的狀態改變接口重新注冊到 resolve 的值對應的 Promise 的 onFulfilled 中,也就是說當前 Promise 實例的狀態要依賴 resolve 的值的 Promise 實例的狀態。
【 Demo 4 的源碼】
執行的結果如下:
[Promse-1]:constructor [Promse-2]:constructor [Promse-1]:then [Promse-3]:constructor [Promse-1]:_handle state= pending [Promse-1]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ] [Promse-3]:then [Promse-4]:constructor [Promse-3]:_handle state= pending [Promse-3]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ] => Promise { callbacks: [], name: 'Promse-4', state: 'pending', value: null } [Promse-1]:_resolve [Promse-1]:_resolve value= getUserId異步請求耗時1秒 [Promse-1]:_handle state= fulfilled getUserId異步請求耗時1秒 [Promse-3]:_resolve [Promse-3]:_resolve value= Promise { callbacks: [], name: 'Promse-2', state: 'pending', value: null } [Promse-2]:then [Promse-5]:constructor [Promse-2]:_handle state= pending [Promse-2]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ] [Promse-2]:_resolve [Promse-2]:_resolve value= getUserName異步請求耗時2秒 [Promse-2]:_handle state= fulfilled [Promse-3]:_resolve [Promse-3]:_resolve value= getUserName異步請求耗時2秒 [Promse-3]:_handle state= fulfilled getUserName異步請求耗時2秒 [Promse-4]:_resolve [Promse-4]:_resolve value= undefined [Promse-5]:_resolve [Promse-5]:_resolve value= undefined
一樣的,我做了一個演示動畫,還原了這個過程:
(Promise 真正的鏈式調用)
“分析Promise鏈式調用”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。