一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - node.js - 解決await在forEach中不起作用的問題

解決await在forEach中不起作用的問題

2022-01-24 16:36文文文文文哥 node.js

這篇文章主要介紹了解決await在forEach中不起作用的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

一、前言

前兩天在項目中用for遍歷的時候遇到了一個坑,花了一天的時間解決。這里就記一下。

二、問題

首先引一個很簡單題目:給一個數組,每隔1s打印出來.這里我把我一開始在項目中的代碼貼出來.(當然這里完全和業務無關的)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const _ = require('lodash');
const echo = async (i) => {
  setTimeout(() => {
    console.log('i===>', i);
  }, 5000);
}
let arrs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const task = async () => {
  _.forEach(arrs, async (i) => {
    await echo(i);
  })
}
const run = async () => {
  console.log('run-start====>date:', new Date().toLocaleDateString())
  await task() ;
  console.log('run-end====>date:', new Date().toLocaleDateString())
}
(async () => {
  console.log('start...')
  await run();
  console.log('end...')
})()
// start...
// run-start====>date: 2018-8-25
// run-end====>date: 2018-8-25
// end...
// i===> 1
// i===> 2
// i===> 3
// i===> 4
// i===> 5
// i===> 6
// i===> 7
// i===> 8
// i===> 9

上面的代碼和輸出已經給出了,很奇怪,這里的await并沒有其效果.一開始因為是加了業務,是我的業務代碼出了問題,然后我就把代碼抽出來了,還是不起作用,當時我是真的對對await懷疑了。

最后還是給出問題的答案:

lodash的forEach和[].forEach不支持await,如果非要一邊遍歷一邊執行await,可使用for-of

這里給出正確的代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const _ = require('lodash');
const echo = async (i) => {
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
      console.log('i===>', i,new Date().toLocaleTimeString());
      resolve(i) ;
    }, 2000);
  })
}
let arrs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const task = async () => {
  // _.forEach(arrs, async (i) => {
  //  await echo(ji) ;
  // })
  // arrs.forEach(async (i )=> {
  //   await echo(i) ;
  // });
  for (const i of arrs) {
    await echo(i) ;
  }
}
const run = async () => {
  console.log('run-start====>date:', new Date().toLocaleDateString())
  await task() ;
  console.log('run-end====>date:', new Date().toLocaleDateString())
}
(async () => {
  console.log('start...')
  await run();
  console.log('end...')
})()
// 輸出
start...
run-start====>date: 2018-8-26
i===> 1 20:51:29
i===> 2 20:51:31
i===> 3 20:51:33
i===> 4 20:51:35
i===> 5 20:51:37
i===> 6 20:51:39
i===> 7 20:51:42
i===> 8 20:51:44
i===> 9 20:51:46
i===> 10 20:51:48
run-end====>date: 2018-8-26
end...

三、總結

當解決問題的時候,有時候可以使用排除法,比方說在這個例子中,我們知道await這個機制肯定是沒問題的,如果真的有問題肯定不會輪到我測出來,那么其實剩下來的問題只能是for遍歷的原因了.

因為我一開始是用lodash實現的,那么就可以想是不是lodash的forEach沒有作(或者做了多余)await處理,此時就可以換種方式試試了,總的來說還是經驗的問題吧。

補充:在 forEach 中使用 async/await 遇到的問題

一、問題描述

前幾天,項目中遇到一個 JavaScript 異步問題:

有一組數據,需要對每一個數據進行一個異步處理,并且希望處理的時候是同步的。

用代碼描述如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 生成數據
const getNumbers = () => {
 return Promise.resolve([1, 2, 3])
}
// 異步處理
const doMulti = num => {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
   if (num) {
    resolve(num * num)
   } else {
    reject(new Error('num not specified'))
   }
  }, 2000)
 })
}
// 主函數
const main = async () => {
 console.log('start');
 const nums = [1, 2, 3];
 nums.forEach(async (x) => {
  const res = await doMulti(x);
  console.log(res);
 });
 console.log('end');
};
// 執行
main();

在這個例子中,通過 forEach 遍歷地將每一個數字都執行 doMulti 操作。代碼執行的結果是:首先會立即打印 start、end 。2 秒后,一次性輸出 1,4,9。

這個結果和我們的預期有些區別,我們是希望每間隔 2 秒,執行一次異步處理,依次輸出 1,4,9。所以當前代碼應該是并行執行了,而我們期望的應該是串行執行。

我們嘗試把 forEach 循環替換成 for 循環:

?
1
2
3
4
5
6
7
8
9
const main = async () => {
 console.log('start');
 const nums = await getNumbers();
 for (const x of nums) {
  const res = await doMulti(x);
  console.log(res);
 }
 console.log('end');
};

執行結果完全符合了預期:依次輸出:start、1, 4, 9, end 。

二、問題分析

思路都是一樣的,只是使用的遍歷方式不一樣而已,為什么會出現這樣的情況呢?在 MDN 上查找了一下 forEach 的 polyfill 參考 MDN-Array.prototype.forEach() :

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.forEach) {
 Array.prototype.forEach = function(callback, thisArg) {
  var T, k;
  if (this == null) {
   throw new TypeError(' this is null or not defined');
  }
  // 1. Let O be the result of calling toObject() passing the
  // |this| value as the argument.
  var O = Object(this);
  // 2. Let lenValue be the result of calling the Get() internal
  // method of O with the argument "length".
  // 3. Let len be toUint32(lenValue).
  var len = O.length >>> 0;
  // 4. If isCallable(callback) is false, throw a TypeError exception.
  // See: http://es5.github.com/#x9.11
  if (typeof callback !== "function") {
   throw new TypeError(callback + ' is not a function');
  }
  // 5. If thisArg was supplied, let T be thisArg; else let
  // T be undefined.
  if (arguments.length > 1) {
   T = thisArg;
  }
  // 6. Let k be 0
  k = 0;
  // 7. Repeat, while k < len
  while (k < len) {
   var kValue;
   // a. Let Pk be ToString(k).
   //  This is implicit for LHS operands of the in operator
   // b. Let kPresent be the result of calling the HasProperty
   //  internal method of O with argument Pk.
   //  This step can be combined with c
   // c. If kPresent is true, then
   if (k in O) {
    // i. Let kValue be the result of calling the Get internal
    // method of O with argument Pk.
    kValue = O[k];
    // ii. Call the Call internal method of callback with T as
    // the this value and argument list containing kValue, k, and O.
    callback.call(T, kValue, k, O);
   }
   // d. Increase k by 1.
   k++;
  }
  // 8. return undefined
 };
}

從上面的 polyfill 中的 setp 7 ,我們可以簡單地理解成下面的步驟:

?
1
2
3
4
5
6
7
Array.prototype.forEach = function (callback) {
 // this represents our array
 for (let index = 0; index < this.length; index++) {
  // We call the callback for each entry
  callback(this[index], index, this);
 };
};

相當于 for 循環執行了這個異步函數,所以是并行執行,導致了一次性全部輸出結果:1,4,9 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const main = async () => {
 console.log('start');
 const nums = await getNumbers();
 // nums.forEach(async (x) => {
 //  const res = await doMulti(x);
 //  console.log(res);
 // });
 for (let index = 0; index < nums.length; index++) {
  (async x => {
   const res = await doMulti(x)
   console.log(res)
  })(nums[index])
 }
 console.log('end');
};

三、解決方案

現在,我們把問題分析清楚了。前面用 for-of 循環來代替 forEach 作為解決方案 ,其實我們也可以改造一下 forEach :

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const asyncForEach = async (array, callback) => {
 for (let index = 0; index < array.length; index++) {
  await callback(array[index], index, array);
 }
}
const main = async () => {
 console.log('start');
 const nums = await getNumbers();
 await asyncForEach(nums, async x => {
  const res = await doMulti(x)
  console.log(res)
 })
 console.log('end');
};
main();

四、Eslint 問題

這時候 Eslint 又報了錯:no-await-in-loop 。關于這一點,Eslint 官方文檔 https://eslint.org/docs/rules/no-await-in-loop 也做了說明。

好的寫法:

?
1
2
3
4
5
6
7
8
9
async function foo(things) {
 const results = [];
 for (const thing of things) {
  // Good: all asynchronous operations are immediately started.
  results.push(bar(thing));
 }
 // Now that all the asynchronous operations are running, here we wait until they all complete.
 return baz(await Promise.all(results));
}

不好的寫法:

?
1
2
3
4
5
6
7
8
async function foo(things) {
 const results = [];
 for (const thing of things) {
  // Bad: each loop iteration is delayed until the entire asynchronous operation completes
  results.push(await bar(thing));
 }
 return baz(results);
}

其實上面兩種寫法沒有什么好壞之分,這兩種寫法的結果是完全不一樣的。Eslint 推薦的 “好的寫法” 在執行異步操作的時候沒有順序的,“不好的寫法” 中有順序,具體需要用哪種寫法還是要根據業務需求來決定。

所以,在文檔的 When Not To Use It 中,Eslint 也提到,如果需要有順序地執行,我們是可以禁止掉該規則的:

In many cases the iterations of a loop are not actually independent of each-other. For example, the output of one iteration might be used as the input to another. Or, loops may be used to retry asynchronous operations that were unsuccessful. Or, loops may be used to prevent your code from sending an excessive amount of requests in parallel. In such cases it makes sense to use await within a loop and it is recommended to disable the rule via a standard ESLint disable comment.

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。如有錯誤或未考慮完全的地方,望不吝賜教。

原文鏈接:https://blog.csdn.net/ssbb1995/article/details/82084800

延伸 · 閱讀

精彩推薦
  • node.js在瀏覽器中,把 Vite 跑起來了!

    在瀏覽器中,把 Vite 跑起來了!

    大家好,我是 ssh,前幾天在推上沖浪的時候,看到 Francois Valdy 宣布他制作了 browser-vite[1],成功把 Vite 成功在瀏覽器中運行起來了。這引起了我的興趣,如...

    前端從進階到入院9282022-01-11
  • node.js詳解node.js創建一個web服務器(Server)的詳細步驟

    詳解node.js創建一個web服務器(Server)的詳細步驟

    這篇文章主要介紹了詳解node.js創建一個web服務器(Server)的詳細步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    王佳斌8952021-12-31
  • node.jsrequire加載器實現原理的深入理解

    require加載器實現原理的深入理解

    這篇文章主要給大家介紹了關于require加載器實現原理的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需...

    隱冬8462022-03-03
  • node.jsk8s node節點重新加入master集群的實現

    k8s node節點重新加入master集群的實現

    這篇文章主要介紹了k8s node節點重新加入master集群的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    Scarborought13922022-01-22
  • node.jsnodejs中使用worker_threads來創建新的線程的方法

    nodejs中使用worker_threads來創建新的線程的方法

    這篇文章主要介紹了nodejs中使用worker_threads來創建新的線程的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友...

    flydean程序那些事8982022-01-06
  • node.jsNode.js 中如何收集和解析命令行參數

    Node.js 中如何收集和解析命令行參數

    這篇文章主要介紹了Node.js 中如何收集和解析命令行參數,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    descire8802021-12-28
  • node.jslinux服務器快速卸載安裝node環境(簡單上手)

    linux服務器快速卸載安裝node環境(簡單上手)

    這篇文章主要介紹了linux服務器快速卸載安裝node環境(簡單上手),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需...

    mose-x8462022-01-22
  • node.jsNode.js ObjectWrap 的弱引用問題

    Node.js ObjectWrap 的弱引用問題

    最近在寫 Node.js Addon 的過程中,遇到了一個問題,然后發現是 ObjectWrap 弱引用導致的,本文介紹一下具體的問題和排查過程,以及 ObjectWrap 的使用問題。...

    編程雜技9852022-01-04
主站蜘蛛池模板: 91精品手机国产在线观 | 免费在线观看小视频 | 校花被老头夺去第一次动图 | 精品国产线拍大陆久久尤物 | 精品综合久久久久久88小说 | 国产果冻传媒 | 99热碰| 国产午夜永久福利视频在线观看 | 大学生宿舍飞机 free | chinese男男gay| 亚洲a视频在线观看 | 大好硬好深好爽想要视频 | 国产精品福利在线观看秒播 | 国产精品福利久久2020 | 乌克兰肛交影视 | 全彩孕交漫画福利啪啪吧 | 麻豆网| 色综合久久天天综合观看 | 农村妇女野外牲交一级毛片 | 丁香五香天堂网 | 草草视频在线观看 | 天天操天天射天天色 | 大胸孕妇孕交pregnantsex 大象视频污 | 久久无码人妻AV精品一区 | 日本护士撒尿 | 国产精品久久久精品视频 | 雪恋电影完整版免费观看 | 亚洲国产香蕉视频欧美 | 国产精品欧美亚洲韩国日本 | 99视频都是精品热在线播放 | 狠狠躁夜夜躁人人爽天天miya | 午夜办公室在线观看高清电影 | 成人伊人青草久久综合网破解版 | 白丝萝莉h| 热99re国产久热在线 | 亚洲四虎在线 | 99九九精品免费视频观看 | 奇米影视888四色首页 | 久久内在线视频精品mp4 | 久久久久久免费观看 | 大胆国模一区二区三区伊人 |