您好,登錄后才能下訂單哦!
異步之Promise
Promise.all
Promise.all接收的promise數組是按順序執行的還是一起執行的,也就是說返回的結果是順序固定的嗎?
目前有兩種答案:
那么我們根據實現來解密:
環境為:
vscode 1.20.1 node v8.9.0 npm v5.6.0
實驗代碼:
// 獲取隨機數,toFixed為四舍五入保留小數,0為保留整數,范圍~1000 const getRandom = () => +(Math.random()*1000).toFixed(0); const asyncTask = (taskID) => new Promise( (resolve) => { // 隨機獲取一次0~1000的隨機數 let timeout = getRandom(); // 打印出傳遞進來的ID號 taskID=1 start. console.log(`taskID=${taskID} start.`); // 設置計時時間,function()等價于 () => {...} setTimeout(function() { // 打印出執行的taskID,和timeout console.log(`taskID=${taskID} finished in time=${timeout}.`); // 異步成功執行 resolve(taskID) }, timeout); }); Promise.all([asyncTask(1),asyncTask(2),asyncTask(3)]) .then(resultList => { console.log('results:',resultList); });
實驗結果如下:
第一次
taskID=1 start. taskID=2 start. taskID=3 start. taskID=2 finished in time=321. taskID=3 finished in time=506. taskID=1 finished in time=932. results: Array(3) [1, 2, 3]
第二次
taskID=1 start. taskID=2 start. taskID=3 start. taskID=1 finished in time=243. taskID=3 finished in time=305. taskID=2 finished in time=792. results: Array(3) [1, 2, 3]
第三次
taskID=1 start. taskID=2 start. taskID=3 start. taskID=3 finished in time=380. taskID=1 finished in time=539. taskID=2 finished in time=782. results: Array(3) [1, 2, 3]
補充知識介紹:
// toFixed() 方法可把 Number 四舍五入為指定小數位數的數字。 NumberObject.toFixed(num) // num 必需。規定小數的位數,是 0 ~ 20 之間的值,包括 0 和 // 20,有些實現可以支持更大的數值范圍。如果省略了該參數,將用 0 代替。
Promise構造函數只有一個參數,該參數是一個函數,被稱作執行器,執行器有2個參數,分別是resolve()和reject(),一個表示成功的回調,一個表示失敗的回調。
new Promise(function(resolve, reject) { setTimeout(() => resolve(5), 0) }).then(v => console.log(v)) // 5
記住,Promise實例只能通過resolve或者reject函數來返回,并且使用then()或者catch()獲取,不能在new Promise里面直接return,這樣是獲取不到Promise返回值的。
由此可見,Promise.all 里的任務列表[asyncTask(1),asyncTask(2),asyncTask(3)]
,我們是按照順序發起的。
但是根據結果來說,它們是異步的,互相之間并不阻塞,每個任務完成時機是不確定的,盡管如此,所有任務結束之
后,它們的結果仍然是按順序地映射到resultList里,這樣就能和Promise.all里的任務列表
[asyncTask(1),asyncTask(2),asyncTask(3)]
一一對應起來。
深入理解Promise.all()
可能看到這里有些人沒有清楚,為什么返回一個數組?
我們在來看一下這段代碼:
Promise.all([asyncTask(1),asyncTask(2),asyncTask(3)]) .then(resultList => { console.log('results:',resultList); });
通常我們在使用異步的時候都是只有一個Promise,現在我們使用all()方法包裝多個Promise實例。
語法很簡單:參數只有一個,可迭代對象,可以是數組,或者Symbol類型等。
Promise.all(iterable).then().catch()
傳入3個Promise實例:
Promise.all([ new Promise(function(resolve, reject) { resolve(1) }), new Promise(function(resolve, reject) { resolve(2) }), new Promise(function(resolve, reject) { resolve(3) }) ]).then(arr => { console.log(arr) // [1, 2, 3] })
那么我們回頭想想應該明白了吧?
因為我們傳入的是數組,那么返回的必須是數組,并且會將講過進行映射。
Promise.race()
語法和all()一樣,但是返回值有所不同,race根據傳入的多個Promise實例,只要有一個實例resolve或者reject,就只返回該結果,其他實例不再執行。
我們簡單看一下例子,返回結果為3,因為我們設置了定時器,第三個Promise執行的最快。
Promise.race([ new Promise(function(resolve, reject) { setTimeout(() => resolve(1), 1000) }), new Promise(function(resolve, reject) { setTimeout(() => resolve(2), 100) }), new Promise(function(resolve, reject) { setTimeout(() => resolve(3), 10) }) ]).then(value => { console.log(value) // 3 })
異步為什么使用箭頭函數
這是我一直困惑的原因,我們將前面的例子進行改造一下。
如下:
const getRandom = () => +(Math.random()*1000).toFixed(0); function test(taskID) { new Promise( (resolve) => { // 隨機獲取一次0~1000的隨機數 let timeout = getRandom(); // 打印出傳遞進來的ID號 console.log(`taskID=${taskID} start.`); setTimeout(function() { console.log(`taskID=${taskID} finished in time=${timeout}.`); resolve(taskID) }, timeout); } ) } Promise.all([test(1),test(2),test(3)]) .then(resultList => { console.log('results:',resultList); });
我們先來看一下結果是怎樣的?
第一次:
taskID=1 start. taskID=2 start. taskID=3 start. results: Array(3) [undefined, undefined, undefined] taskID=1 finished in time=460. taskID=2 finished in time=704. taskID=3 finished in time=883.
第二次:
taskID=1 start. taskID=2 start. taskID=3 start. results: Array(3) [undefined, undefined, undefined] taskID=2 finished in time=17. taskID=3 finished in time=212. taskID=1 finished in time=612.
第三次:
taskID=1 start. taskID=2 start. taskID=3 start. results: Array(3) [undefined, undefined, undefined] taskID=3 finished in time=130. taskID=1 finished in time=256. taskID=2 finished in time=593.
實驗還是要至少做上3次以上才有說服力。
通過輸出結果我們能夠看出返回的數組內的數據都為undefined。我們就要找出這個原因,那就是找到了為什么要使用箭頭函數。
首先我通過調試來查找
如圖:
程序首先打印出了
taskID=1 start.
taskID=2 start.
taskID=3 start.
說明一定是先執行了
console.log(`taskID=${taskID} start.`);
所以我們在這段打上斷點進行一步一步調試,如下:
根據上圖我們可以看出console.log(taskID=${taskID} start.
)每次都會被執行,setTimeout也會被執行,但是3次之后,就會直接開始執行.then(),所以我們找到了原因,Promise.all()這時并沒有等待返回完整的數據就執行了.then(),沒有等到resolve就開始執行了。
說明這里面出現了異常,而這個異常就是由于Promise.all()內的參數,存在函數,造成this混淆,所以我們要使用對象,更準確的說法就是實例。
注意:
以這段代碼為例:
var p1 = Promise.resolve(1), p2 = Promise.resolve(2), p3 = Promise.resolve(3); Promise.all([p1, p2, p3]).then(function (results) { console.log(results); // [1, 2, 3] });
在上面的方法中,promise數組中所有的promise實例都變為resolve的時候,該方法才會返回,并將所有結果傳遞results數組中。promise數組中任何一個promise為reject的話,則整個Promise.all調用會立即終止,并返回一個reject的新的promise對象。reject使用示例如下:
var p1 = Promise.resolve(1), p2 = Promise.reject(2), p3 = Promise.resolve(3); Promise.all([p1, p2, p3]).then(function (results) { //then方法不會被執行 console.log(results); }).catch(function (e){ //catch方法將會被執行,輸出結果為:2 console.log(2); });
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。