博客 / 詳情

返回

【為什麼】使用 reduce 按順序執行 promise 有效?💭

我是月弦笙音,今天給大家分享  為什麼使用 reduce() 按順序解析 promise 有效 ,嘎嘎的😍,看下面

在不使用對象的情況下編寫異步 js 很像閉着眼睛烤蛋糕。這是可以做到的,但它會很混亂,你最終可能會shao到自己。😂Promise

一、開題展示

我不會説這是必要的,但你明白了。真是太好了😂。不過,有時它需要一點幫助來解決一些獨特的挑戰,比如當你試圖按順序解決一堆承諾時,一個接一個。 例如,當你通過 AJAX 進行某種批處理時,這樣的技巧非常方便。你希望服務器處理一堆事情,但不是一次處理所有事情,因此隨着時間的推移,你將處理間隔開來。

排除有助於簡化此任務的包(如 Caolan McMahon 的異步庫),最常建議的順序解析 promise 的解決方案是獲取一個事物的集合,並將它們簡化為單個值,如下所示:Array.prototype.reduce()

let result = [1,2,5].reduce((accumulator, item) => {
  return accumulator + item;
}, 0); // <-- Our initial value.

console.log(result); // 8

但是,當用於我們的某種目的時,設置看起來更像是這樣:reduce()

let userIDs = [1,2,3];

userIDs.reduce( (previousPromise, nextID) => {
  return previousPromise.then(() => {
    return methodThatReturnsAPromise(nextID);
  });
}, Promise.resolve());

或者,以更現代化的格式:

let userIDs = [1,2,3];

userIDs.reduce( async (previousPromise, nextID) => {
  await previousPromise;
  return methodThatReturnsAPromise(nextID);
}, Promise.resolve());

這很簡潔!但在很長一段時間裏,我只是知道這個解決方案,並將那段代碼複製到我的項目中,因為它“有效”。
所以!
這篇文章是我要説清楚兩件事:😍

1. 為什麼這種方法會奏效?

2. 為什麼我們不能用其他方法來做同樣的事情?Array

二、 為什麼這種方法會奏效?

請記住,它的主要目的是將一堆東西“簡化”為一個東西,它通過在循環運行時將結果存儲來實現這一點。但這不一定是數字。循環可以返回它想要的任何內容(如 promise),並在每次迭代中通過回調回收該值。

值得注意的是,無論值是什麼,循環本身都不會改變其行為,包括其執行速度。它只是在線程允許的範圍內以最快的速度在集合中運行。reduce() accumulator

這很難理解,因為它可能違背了你認為在這個循環中發生的事情(至少,它對我來説是這樣)。當我們使用它來按順序解析 promise 時,reduce() 循環實際上根本沒有減慢速度。 它是完全同步的,像往常一樣儘可能快地做正常的事情。

看以下代碼片段,並注意回調中返回的 promise 如何完全不阻礙循環的進度!😎

function methodThatReturnsAPromise(nextID) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {

      console.log(`Resolve! ${dayjs().format('hh:mm:ss')}`);

      resolve();
    }, 1000);
  });
}

[1,2,3].reduce( (accumulatorPromise, nextID) => {

  console.log(`Loop! ${dayjs().format('hh:mm:ss')}`);

  return accumulatorPromise.then(() => {
    return methodThatReturnsAPromise(nextID);
  });
}, Promise.resolve());

在我們的控制枱中:

"Loop! 11:28:06"
"Loop! 11:28:06"
"Loop! 11:28:06"
"Resolve! 11:28:07"
"Resolve! 11:28:08"
"Resolve! 11:28:09"

promise 按照我們預期的順序解析,但循環本身是快速、穩定和同步的。在查看了 的 MDN polyfill 之後,這是有道理的。循環一遍又一遍地觸發,沒有什麼異步的,這就是引擎蓋下發生的事情:reduce() while() callback()

while (k < len) {
  if (k in o) {
    value = callback(value, o[k], k, o);
  }
  k++;
}

真正的魔力就發生在這兒:

return previousPromise.then(() => {
  return methodThatReturnsAPromise(nextID)
});

每次觸發回調時,我們都會返回一個 promise,該 promise 解析為另一個 promise。雖然不會等待任何解決方案發生,但它確實提供的優勢是能夠在每次運行後將某些內容傳遞迴同一個回調,這是獨有的功能。因此,我們能夠構建一個承諾鏈,這些承諾可以解析為更多的承諾,使一切都變得美好和有序:reduce()

look!👀

new Promise( (resolve, reject) => {
  // Promise #1
  
  resolve();
}).then( (result) => { 
  // Promise #2
  
  return result;
}).then( (result) => { 
  // Promise #3
  
  return result;
}); // ... and so on!

所以!這些也應該揭示為什麼我們不能在每次迭代中只返回一個單一的新承諾。由於循環是同步運行的,因此每個 promise 都會立即觸發,而不是等待之前創建的 promise

[1,2,3].reduce( (previousPromise, nextID) => {

  console.log(`Loop! ${dayjs().format('hh:mm:ss')}`);
  
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(`Resolve! ${dayjs().format('hh:mm:ss')}`);
      resolve(nextID);
    }, 1000);
  });
}, Promise.resolve());

在我們的控制枱中:

"Loop! 11:31:20"
"Loop! 11:31:20"
"Loop! 11:31:20"
"Resolve! 11:31:21"
"Resolve! 11:31:21"
"Resolve! 11:31:21"

是否可以等到所有處理完成後再做其他事情?是的。

同步性並不意味着你不能在每個項目都完全處理完畢後在進行下一步操作。看:reduce()

function methodThatReturnsAPromise(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(`Processing ${id}`);
      resolve(id);
    }, 1000);
  });
}

let result = [1,2,3].reduce( (accumulatorPromise, nextID) => {
  return accumulatorPromise.then(() => {
    return methodThatReturnsAPromise(nextID);
  });
}, Promise.resolve());

result.then(e => {
  console.log("Resolution is complete! Let's party.")
});

由於我們在回調中返回的只是一個鏈式 promise,因此當循環結束時,我們得到的就是:一個 promise。在那之後,我們可以隨心所欲地處理它,即使在它已經走完了很久之後。reduce()

三、那為神馬其他 Array 方法都不起作用?💢

請記住,在js的引擎下,我們不會等待回調完成,然後再進入下一個項目。它是完全同步的。所有這些其他方法也是如此:reduce()

  • Array.prototype.map()
  • Array.prototype.forEach()
  • Array.prototype.filter()
  • Array.prototype.some()
  • Array.prototype.every()

但是reduce()很特別😎

我們發現,之所以reduce對我們有用,是因為我們能夠將某些內容直接返回到我們相同的回調(即 promise),然後我們可以通過將它解析為另一個 promise 來構建它。

但是,對於所有這些其他方法,我們無法將參數傳遞給回調返回的回調。相反,這些回調參數中的每一個都是預先確定的,因此我們無法利用它們來實現順序承諾解析之類的事情。

[1,2,3].map((item, [index, array]) => [value]);
[1,2,3].filter((item, [index, array]) => [boolean]);
[1,2,3].some((item, [index, array]) => [boolean]);
[1,2,3].every((item, [index, array]) => [boolean]);

四、 結語

ok!我想這個為什麼使用 reduce() 按順序解析 promise 有效我已經説的很明白了。

至少,我希望以上這些有助於闡明為什麼唯有reduce有資格以這種方式處理承諾,並可能讓你更好地瞭解常見方法在後台的運作方式。reduce() Array

感謝瀏覽本文,共同進步🤞,若有更好的建議,歡迎評論區討論哈🌹。

如果覺得這篇乾貨很有用,可以加個關關🥰,點個贊贊🥰,後面我會繼續分享更多幹貨!感謝支持!😍

本文參與了SegmentFault 思否寫作挑戰賽活動,歡迎正在閲讀的你也加入。
user avatar lanlanjintianhenhappy 頭像 guizimo 頭像 yaofly 頭像 huishou 頭像 dujing_5b7edb9db0b1c 頭像 chongdianqishi 頭像 coderleo 頭像 uncletong_doge 頭像 201926 頭像 lesini 頭像 iymxpc3k 頭像 niumingxin 頭像
50 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.