您好,登錄后才能下訂單哦!
如何在Nodejs中使用forEach?相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。
利用數(shù)組對(duì)象方法push/shift可實(shí)現(xiàn)隊(duì)列先進(jìn)先出特性,例如:
>a=[] [] >a.push(2.3.4) 3 >a.push(2) 3 >a [2.3.4.2] >a.shift() 2 >a >[3.4.2]
對(duì)數(shù)組的刪除操作有兩種常見方式:delete和使用splice方法,需要明確他們的區(qū)別。
操作/方法 | 說明 |
---|---|
splice | 刪除并返回指定的數(shù)組元素,數(shù)組本身長度會(huì)改變;但不會(huì)free元素對(duì)象 |
delete | 刪除(free)元素對(duì)象,數(shù)組元素不變,值變?yōu)閡ndefined |
如果要從數(shù)組中徹底刪除某個(gè)元素,使用splice即可:
> a=[1,2,3] [ 1, 2, 3 ] > a.splice(1,1) [ 2 ] > a [ 1, 3 ] > a.length 2 > a.forEach(function(item, index){console.info("index[", index,"]:", item)}); index[ 0 ]: 1 index[ 1 ]: 3 undefined >
那么,當(dāng)使用delete刪除某個(gè)元素對(duì)象后,此時(shí)執(zhí)行forEach的效果是什么?
測試結(jié)果如下
> a=[1,2,3] [ 1, 2, 3 ] > delete a[1] true > a [ 1, , 3 ] > a.length 3 > a.forEach(function(item, index){console.info("index[", index,"]:", item)}); index[ 0 ]: 1 index[ 2 ]: 3 undefined
從測試結(jié)果來看,forEach并不會(huì)遍歷到值為undefined的哪一項(xiàng)。這在實(shí)際應(yīng)用中如何判斷forEach是否結(jié)束是一大挑戰(zhàn)。
解決配合forEach的異步特性應(yīng)用,可為數(shù)組添加prototype來自行管理設(shè)置有效數(shù)據(jù);
效果如下:
> a=[1,2,3] [ 1, 2, 3 ] > a.validnum=3 3 > delete a[2] true > a.validnum=2 2 > a [ 1, 2, , validnum: 2 ] > a.length 3 > a.validnum 2 > a.forEach(function(item, index){console.info("index[", index,"]:", item)}); index[ 0 ]: 1 index[ 1 ]: 2 undefined >
補(bǔ)充:Node.js 數(shù)組 forEach 同步處理上下文語句
習(xí)慣了C語言系的思維方式,剛接觸Node.js,它的異步處理讓我頭大。
寫代碼遇到這么一個(gè)場景,需要循環(huán)對(duì)一個(gè)數(shù)組中的元素進(jìn)行處理,全部處理完成后再執(zhí)行一個(gè)last操作。但是JS的異步特性會(huì)使這個(gè)last語句先執(zhí)行,所以花點(diǎn)時(shí)間研究研究forEach。
Talk is cheap. Show me the code.
forEach用于對(duì)數(shù)組結(jié)構(gòu)進(jìn)行遍歷,看到有人說forEach底層是用for實(shí)現(xiàn)的,沒深究,起碼效果上看是一樣的。forEach的回調(diào)函數(shù)3個(gè)參數(shù)分別是:值、序號(hào)和原數(shù)組。序號(hào)從0開始。
(() => { let arr = [2, 3, 1]; arr.forEach(function (value, index, array) { console.log(value); console.log(index); console.log(array); console.log('-----'); }); })();
Output
2 0 [ 2, 3, 1 ] ----- 3 1 [ 2, 3, 1 ] ----- 1 2 [ 2, 3, 1 ] -----
從結(jié)果上看forEach多次循環(huán)之間是同步的,也就是說都是按順序執(zhí)行的。但是一想到它是JS就感覺不可能同步的。。可以驗(yàn)證一下。
這次在forEach加個(gè)定時(shí)任務(wù),每次循環(huán)操作都延時(shí)value相關(guān)的時(shí)間,模擬比較耗時(shí)的操作。
(() => { let arr = [2, 3, 1]; arr.forEach(function (value, index, array) { setTimeout(function () { console.log(value); }, value*100); }); })();
Output
1 2 3
從結(jié)果可以看出耗時(shí)最短的任務(wù)先完成,每次循環(huán)的任務(wù)并不是按循環(huán)的先后順序執(zhí)行的,也就是說異步處理多次循環(huán)。
回到開始說到的問題了,且不管多次循環(huán)是不是按順序執(zhí)行,我需要forEach中的所有任務(wù)都完成后執(zhí)行一條數(shù)據(jù)來通知我任務(wù)全部完成了。
(() => { let arr = [2, 3, 1]; arr.forEach(function (value, index, array) { setTimeout(function () { console.log(value); }, value*100); }); console.log('All the work is done'); })();
Output
All the work is done 1 2 3
從結(jié)果來看,上下文的語句也不是同步的,forEach循環(huán)中的任務(wù)沒有完成就通知所有任務(wù)都完成了,顯然不符合預(yù)期。
針對(duì)這個(gè)問題看了好多個(gè)博客,都沒有找到合適的解決方法,最后只能想到用Promise.all來勉強(qiáng)實(shí)現(xiàn)這個(gè)功能。
把上面的代碼改成Promise.all的結(jié)構(gòu)。每個(gè)循環(huán)中執(zhí)行結(jié)束調(diào)用resolve(),我們知道Promise.all的then函數(shù),只有所有的Promise都執(zhí)行完成才會(huì)觸發(fā),這樣好像能滿足我們的需求。
(() => { let arr = [2, 3, 1]; let proArr = []; arr.forEach(function (value, index) { proArr[index] = new Promise(function (resolve) { setTimeout(function () { console.log(value); resolve(); }, value*100); }); }); Promise.all(proArr).then(()=>{ console.log('All the work is done'); }) })();
Output
1 2 3 All the work is done
從結(jié)果來看,滿足了我們的需求。
想到JS異步特性,突然發(fā)現(xiàn)可能這個(gè)方法還存在個(gè)問題。
這里每次 forEach 剛進(jìn)入就對(duì) Promise 數(shù)組進(jìn)行了賦值操作,這個(gè)操作時(shí)間應(yīng)該非常短,循環(huán)3次都賦值完成后才調(diào)用最后的Promise.all語句。
但是如果這個(gè)數(shù)組非常大,這個(gè)循環(huán)賦值的操作非常耗時(shí)間的話,假如只完成了一半的賦值操作,那么執(zhí)行最后這個(gè) Promise.all 的時(shí)候傳入的 Promise 數(shù)組可能并不是包含所有 Promise 的數(shù)組。
這樣的話 Promise.all 等待的就只有一半的操作,Promise.all 等待的時(shí)候,這個(gè)數(shù)組后面被賦值的 Promise 不知道會(huì)不會(huì)被等待。
剛接觸JS不明白實(shí)現(xiàn)機(jī)制,只能實(shí)驗(yàn)來驗(yàn)證一下是否存在這個(gè)問題。接下來用把這個(gè)數(shù)組弄大一些,請(qǐng)?jiān)徫矣米钌倒鲜降姆绞礁愦笏?/p>
(() => { let arr = [2, 3, 1, 2, 3, 1, 2, 3, 1, 2]; // 10 arr= arr.concat(arr); // 2^1 * 10 arr= arr.concat(arr); // 2^2 * 10 arr= arr.concat(arr); // 2^3 arr= arr.concat(arr); // 2^4 arr= arr.concat(arr); // 2^5 arr= arr.concat(arr); arr= arr.concat(arr); arr= arr.concat(arr); arr= arr.concat(arr); arr= arr.concat(arr); // 2^10 arr= arr.concat(arr); arr= arr.concat(arr); arr= arr.concat(arr); arr= arr.concat(arr); arr= arr.concat(arr); // 2^15 arr= arr.concat(arr); arr= arr.concat(arr); // 2^17 * 10 // arr= arr.concat(arr); // 2^18 * 10 console.log(arr.length); let proArr = []; arr.forEach(function (value, index) { proArr[index] = new Promise(function (resolve) { setTimeout(function () { console.log(value); resolve(); }, value*100); }); }); Promise.all(proArr).then(()=>{ console.log('All the work is done'); console.log(arr.length); }).catch(function (err) { console.log(err); }) })();
經(jīng)過測試在我這個(gè)電腦上當(dāng)數(shù)組長度為2^18 * 10的時(shí)候,Promise報(bào)錯(cuò) RangeError: Too many elements passed to Promise.all。
當(dāng)數(shù)組長度為2^17 * 10 即2621440的時(shí)候,會(huì)正常運(yùn)行。測試了幾次,最后的執(zhí)行命令輸出的All the work is done始終在最后輸出(因?yàn)榻K端緩沖區(qū)太小,所以使用node xx.js > log.txt重定向的方式把輸出結(jié)果重定向到文件查看)。
當(dāng)然應(yīng)用中也不會(huì)有這么大的數(shù)組,從結(jié)果看的話,就是實(shí)際應(yīng)用中不存在上面考慮可能出現(xiàn)的問題。
也就是說可以用 Promise.all 實(shí)現(xiàn) forEach 上下文語句同步處理。
看完上述內(nèi)容,你們掌握如何在Nodejs中使用forEach的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。