ฉันมีคำถามเกี่ยวกับการArray.forEach
ติดตั้ง JavaScript แบบดั้งเดิม: มันทำงานแบบอะซิงโครนัสหรือไม่? ตัวอย่างเช่นถ้าฉันโทร:
[many many elements].forEach(function () {lots of work to do})
สิ่งนี้จะไม่มีการปิดกั้นหรือไม่
ฉันมีคำถามเกี่ยวกับการArray.forEach
ติดตั้ง JavaScript แบบดั้งเดิม: มันทำงานแบบอะซิงโครนัสหรือไม่? ตัวอย่างเช่นถ้าฉันโทร:
[many many elements].forEach(function () {lots of work to do})
สิ่งนี้จะไม่มีการปิดกั้นหรือไม่
คำตอบ:
ไม่มันกำลังปิดกั้น มีลักษณะที่เป็นสเปคของขั้นตอนวิธี
อย่างไรก็ตามอาจมีการเข้าใจการใช้งานที่ง่ายขึ้นบนMDN :
if (!Array.prototype.forEach)
{
Array.prototype.forEach = function(fun /*, thisp */)
{
"use strict";
if (this === void 0 || this === null)
throw new TypeError();
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== "function")
throw new TypeError();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in t)
fun.call(thisp, t[i], i, t);
}
};
}
หากคุณต้องใช้รหัสจำนวนมากสำหรับแต่ละองค์ประกอบคุณควรพิจารณาใช้วิธีการอื่น:
function processArray(items, process) {
var todo = items.concat();
setTimeout(function() {
process(todo.shift());
if(todo.length > 0) {
setTimeout(arguments.callee, 25);
}
}, 25);
}
แล้วเรียกมันว่า:
processArray([many many elements], function () {lots of work to do});
นี่จะไม่ใช่การปิดกั้นในตอนนั้น ตัวอย่างที่จะนำมาจากHigh Performance JavaScript
forEach
ไม่ได้บล็อกบนawait
ข้อความสั่งต่างๆและคุณควรใช้การfor
วนซ้ำ: stackoverflow.com/questions/37962880/…
await
ภายในasync
เท่านั้น แต่forEach
ไม่รู้ว่าฟังก์ชั่น async คืออะไร โปรดทราบว่าฟังก์ชั่น async เป็นเพียงฟังก์ชั่นการคืนสัญญา คุณคาดหวังว่าforEach
จะได้รับสัญญาที่ส่งคืนจากการติดต่อกลับหรือไม่ forEach
ละเว้นค่าส่งคืนจากการเรียกกลับทั้งหมด มันจะสามารถจัดการกับการโทรกลับ async ได้หากมันเป็น async เท่านั้น
หากคุณต้องการรุ่นที่Array.forEach
คล้ายกันแบบอะซิงโครนัสและคล้ายคลึงกันพวกมันมีอยู่ในโมดูล 'async' ของ Node.js: http://github.com/caolan/async ... เป็นโบนัสโมดูลนี้ยังใช้งานได้ในเบราว์เซอร์ .
async.each(openFiles, saveFile, function(err){
// if any of the saves produced an error, err would equal that error
});
eachSeries
แทน
มีรูปแบบทั่วไปสำหรับทำการคำนวณที่หนักมากในโหนดที่อาจใช้กับคุณ ...
โหนดเป็นเธรดเดี่ยว (เป็นตัวเลือกการออกแบบโดยเจตนาดูที่ Node.js คืออะไร ) นี่หมายความว่ามันสามารถใช้ประโยชน์จากแกนเดียวเท่านั้น กล่องโมเดิร์นมี 8, 16 หรือคอร์ที่มากขึ้นดังนั้นสิ่งนี้อาจทำให้ 90% ของเครื่องว่าง รูปแบบที่พบบ่อยสำหรับการให้บริการส่วนที่เหลือคือการยิงขึ้นกระบวนการโหนดหนึ่งต่อคอและใส่เหล่านี้อยู่เบื้องหลัง balancer โหลดท้องถิ่นเช่นhttp://nginx.org/
การแยกเด็ก - สำหรับสิ่งที่คุณพยายามจะทำมีรูปแบบทั่วไปอีกอย่างหนึ่งคือการแยกกระบวนการของเด็กออกเพื่อยกของหนัก ข้อดีคือกระบวนการของเด็กสามารถทำการคำนวณอย่างหนักในพื้นหลังในขณะที่กระบวนการหลักของคุณตอบสนองต่อเหตุการณ์อื่น ๆ สิ่งที่จับได้คือคุณไม่สามารถ / ไม่ควรแชร์หน่วยความจำกับกระบวนการลูกนี้ (ไม่ใช่โดยไม่มีการคัดค้านและโค้ดเนทีฟจำนวนมาก); คุณต้องส่งข้อความ สิ่งนี้จะทำงานได้อย่างสวยงามหากขนาดของข้อมูลอินพุตและเอาต์พุตของคุณมีขนาดเล็กเมื่อเทียบกับการคำนวณที่ต้องดำเนินการ คุณสามารถเริ่มกระบวนการ child node.js และใช้รหัสเดียวกันกับที่คุณใช้ก่อนหน้านี้
ตัวอย่างเช่น:
var child_process = require ('child_process'); ฟังก์ชัน run_in_child (อาร์เรย์, cb) { var process = child_process.exec ('node libfn.js', ฟังก์ชัน (err, stdout, stderr) { var output = JSON.parse (stdout); cb (ข้อผิดพลาดเอาต์พุต); }); process.stdin.write (JSON.stringify (อาร์เรย์), 'utf8'); process.stdin.end (); }
Array.forEach
มีไว้สำหรับการคำนวณสิ่งต่าง ๆ ที่ไม่รอและไม่มีอะไรที่จะได้รับการคำนวณแบบอะซิงโครนัสในลูปเหตุการณ์ (webworkers เพิ่มการประมวลผลแบบมัลติหากคุณต้องการการคำนวณแบบมัลติคอร์) หากคุณต้องการรอให้งานหลาย ๆ เสร็จสิ้นให้ใช้ตัวนับซึ่งคุณสามารถใส่ในคลาสเซมาฟอร์ได้
แก้ไข 2018/10/11: ดูเหมือนว่ามีโอกาสที่ดีมาตรฐานที่อธิบายด้านล่างอาจจะไม่ผ่านไปพิจารณาpipelineingเป็นทางเลือกอื่น (ไม่ทำงานเหมือนกันทุกประการ แต่อาจใช้วิธีการในคฤหาสน์แบบเดียวกัน)
นี่คือเหตุผลที่ฉันตื่นเต้นกับ es7 ในอนาคตคุณจะสามารถทำบางอย่างเช่นรหัสด้านล่าง (รายละเอียดบางอย่างไม่สมบูรณ์ดังนั้นใช้ด้วยความระมัดระวังฉันจะพยายามทำให้ทันสมัยอยู่เสมอ) แต่โดยพื้นฐานแล้วใช้ตัวดำเนินการ :: ผูกใหม่คุณจะสามารถเรียกใช้เมธอดบนวัตถุราวกับว่าต้นแบบของวัตถุมีวิธีการ เช่น [Object] :: [Method] โดยปกติแล้วคุณจะเรียก [Object] [ObjectsMethod]
หมายเหตุทำวันนี้ (24 ก.ค. 16) และมีมันทำงานในเบราว์เซอร์ทั้งหมดที่คุณจะต้อง transpile รหัสของคุณสำหรับการทำงานต่อไปนี้: นำเข้า / ส่งออก , ฟังก์ชั่นลูกศร , สัญญา , Async / รอและที่สำคัญที่สุดฟังก์ชั่นการผูก โค้ดด้านล่างสามารถ modfied ที่จะใช้เพียงผูกฟังก์ชั่นถ้า nessesary ทุกฟังก์ชั่นนี้สามารถใช้ได้อย่างเรียบร้อยในวันนี้โดยใช้Babel
YourCode.js (โดยที่ 'งานจำนวนมากที่ต้องทำ ' เพียงแค่ส่งคืนสัญญาแก้ไขเมื่องานอะซิงโครนัสเสร็จสิ้น)
import { asyncForEach } from './ArrayExtensions.js';
await [many many elements]::asyncForEach(() => lots of work to do);
ArrayExtensions.js
export function asyncForEach(callback)
{
return Promise.resolve(this).then(async (ar) =>
{
for(let i=0;i<ar.length;i++)
{
await callback.call(ar, ar[i], i, ar);
}
});
};
export function asyncMap(callback)
{
return Promise.resolve(this).then(async (ar) =>
{
const out = [];
for(let i=0;i<ar.length;i++)
{
out[i] = await callback.call(ar, ar[i], i, ar);
}
return out;
});
};
นี่เป็นฟังก์ชั่นแบบอะซิงโครนัสสั้น ๆ ที่ใช้โดยไม่ต้องใช้ libs ของบุคคลที่สาม
Array.prototype.each = function (iterator, callback) {
var iterate = function () {
pointer++;
if (pointer >= this.length) {
callback();
return;
}
iterator.call(iterator, this[pointer], iterate, pointer);
}.bind(this),
pointer = -1;
iterate(this);
};
มีแพคเกจใน NPM สำหรับง่ายคือตรงกันสำหรับแต่ละลูป
var forEachAsync = require('futures').forEachAsync;
// waits for one request to finish before beginning the next
forEachAsync(['dogs', 'cats', 'octocats'], function (next, element, index, array) {
getPics(element, next);
// then after all of the elements have been handled
// the final callback fires to let you know it's all done
}).then(function () {
console.log('All requests have finished');
});
นอกจากนี้รูปแบบอื่นสำหรับAllAsync
เป็นไปได้ที่จะเขียนโค้ดแม้แต่วิธีแก้ปัญหาเช่นนี้:
var loop = function(i, data, callback) {
if (i < data.length) {
//TODO("SELECT * FROM stackoverflowUsers;", function(res) {
//data[i].meta = res;
console.log(i, data[i].title);
return loop(i+1, data, errors, callback);
//});
} else {
return callback(data);
}
};
loop(0, [{"title": "hello"}, {"title": "world"}], function(data) {
console.log("DONE\n"+data);
});
ในทางกลับกันมันช้ากว่า "สำหรับ" มาก
มิฉะนั้นห้องสมุด Async ที่ยอดเยี่ยมสามารถทำได้: https://caolan.github.io/async/docs.html#each
นี่คือตัวอย่างเล็ก ๆ ที่คุณสามารถรันเพื่อทดสอบ:
[1,2,3,4,5,6,7,8,9].forEach(function(n){
var sum = 0;
console.log('Start for:' + n);
for (var i = 0; i < ( 10 - n) * 100000000; i++)
sum++;
console.log('Ended for:' + n, sum);
});
มันจะสร้างบางสิ่งเช่นนี้ (ถ้าใช้เวลาน้อยลง / มากเกินไปให้เพิ่ม / ลดจำนวนการวนซ้ำ):
(index):48 Start for:1
(index):52 Ended for:1 900000000
(index):48 Start for:2
(index):52 Ended for:2 800000000
(index):48 Start for:3
(index):52 Ended for:3 700000000
(index):48 Start for:4
(index):52 Ended for:4 600000000
(index):48 Start for:5
(index):52 Ended for:5 500000000
(index):48 Start for:6
(index):52 Ended for:6 400000000
(index):48 Start for:7
(index):52 Ended for:7 300000000
(index):48 Start for:8
(index):52 Ended for:8 200000000
(index):48 Start for:9
(index):52 Ended for:9 100000000
(index):45 [Violation] 'load' handler took 7285ms
ใช้Promise.eachของไลบรารีBluebird
Promise.each(
Iterable<any>|Promise<Iterable<any>> input,
function(any item, int index, int length) iterator
) -> Promise
วิธีนี้วนซ้ำกว่าอาร์เรย์หรือสัญญาของอาร์เรย์ซึ่งมีสัญญา (หรือการผสมผสานของสัญญาและค่า) กับฟังก์ชั่นiterator ที่กำหนดด้วยลายเซ็น(ค่าดัชนีความยาว)โดยที่ค่าเป็นค่าที่ได้รับการแก้ไขของ สัญญาที่เกี่ยวข้องในอาร์เรย์อินพุต การวนซ้ำเกิดขึ้นอย่างเป็นลำดับหากฟังก์ชั่นตัววนซ้ำส่งคืนสัญญาหรือหลังจากนั้นผลลัพธ์ของสัญญาจะถูกรอก่อนดำเนินการต่อด้วยการทำซ้ำครั้งถัดไป หากสัญญาใด ๆ ในอาร์เรย์อินพุตถูกปฏิเสธดังนั้นสัญญาที่ส่งคืนจะถูกปฏิเสธเช่นกัน
หากทั้งหมดของการทำซ้ำแก้ไขเรียบร้อยแล้วPromise.each มติให้อาร์เรย์เดิมไม่แปร อย่างไรก็ตามหากการทำซ้ำครั้งเดียวปฏิเสธหรือเกิดข้อผิดพลาดPromise.eachจะหยุดการดำเนินการทันทีและจะไม่ประมวลผลการทำซ้ำอีกต่อไป ข้อผิดพลาดหรือค่าปฏิเสธถูกส่งคืนในกรณีนี้แทนอาร์เรย์เดิม
วิธีนี้มีไว้เพื่อใช้สำหรับผลข้างเคียง
var fileNames = ["1.txt", "2.txt", "3.txt"];
Promise.each(fileNames, function(fileName) {
return fs.readFileAsync(fileName).then(function(val){
// do stuff with 'val' here.
});
}).then(function() {
console.log("done");
});