У мене є питання щодо власної 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});
Це б тоді не блокувало. Приклад взято з високоефективного JavaScript .
Інший варіант може бути веб-працівниками .
forEach
це НЕ блок на await
заяви наприклад , і ви швидше повинні використовувати for
цикл: stackoverflow.com/questions/37962880 / ...
await
внутрішні async
функції. Але forEach
не знаю, що таке функції асинхронізації. Майте на увазі, що функції асинхронізації - це лише функції, що повертають обіцянку. Чи очікуєте ви forEach
виконати обіцянку, повернуту з зворотного дзвінка? forEach
повністю ігнорує повернене значення з зворотного дзвінка. Він міг би обробляти зворотний зв'язок з асинхронією лише в тому випадку, якщо він був саме асинхронізацією.
Якщо вам потрібна асинхронна версія 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, яка може бути застосовна для вас ...
Вузол є однопоточним (про обдуманий вибір дизайну див. Що таке Node.js? ); це означає, що він може використовувати лише одне ядро. Сучасні ящики мають 8, 16 або навіть більше ядер, тому це може залишити 90 +% машини в режимі очікування. Загальною схемою для служби REST є запуск одного вузла на одне ядро та їх розміщення позаду локального балансира навантаження, наприклад http://nginx.org/ .
Розсипання дитини - Для того, що ви намагаєтеся зробити, існує ще одна поширена модель, яка змушує дитину робити важкі підйоми. Перевага полягає в тому, що дочірній процес може робити важкі обчислення у фоновому режимі, поки ваш батьківський процес реагує на інші події. Проблема полягає в тому, що ви не можете / не повинні ділитися пам’яттю з цим дочірнім процесом (не маючи багато контурів і деякого нативного коду); ви повинні передавати повідомлення. Це буде добре працювати, якщо розмір вхідних та вихідних даних невеликий порівняно з обчисленнями, які необхідно виконати. Ви навіть можете запустити дочірній процес node.js і використовувати той самий код, який ви використовували раніше.
Наприклад:
var child_process = вимагати ('child_process'); функція run_in_child (масив, cb) { var process = child_process.exec ('вузол libfn.js', функція (помилка, stdout, stderr) { var вихід = JSON.parse (stdout); cb (помилка, вихід); }); process.stdin.write (JSON.stringify (масив), 'utf8'); process.stdin.end (); }
Array.forEach
призначений для обчислення матеріалів, які не чекають, і нічого не можна отримати, роблячи обчислення асинхронними в циклі подій (веб-працівники додають багатопроцесорну обробку, якщо вам потрібні багатоядерні обчислення). Якщо ви хочете дочекатися закінчення кількох завдань, скористайтеся лічильником, який можна загорнути в семафорний клас.
Редагувати 2018-10-11: Схоже, є хороший шанс, що описаний нижче стандарт може не пройти, розгляньте трубопровід як альтернативу (не так само, але методи можуть бути реалізовані в подібній садибі).
Саме тому я схвильований щодо es7, в майбутньому ви зможете зробити щось на зразок коду нижче (деякі технічні характеристики не завершені, тому використовуйте з обережністю, я постараюсь бути в курсі цього). Але в основному, використовуючи новий :: bind-оператор, ви зможете запустити метод на об’єкт так, ніби прототип об'єкта містить метод. наприклад, [Object] :: [Метод], де зазвичай ви б назвали [Object]. [ObjectsMethod]
Зауважте, щоб зробити це сьогодні (24 липня-16), і щоб він працював у всіх веб-переглядачах, вам потрібно буде скопіювати код для наступних функціональних можливостей: Імпорт / Експорт , функції стрілок , Обіцянки , Асинхрон / Очікування та найголовніше функція прив'язки . Код нижче може бути модифікований, щоб використовувати лише функцію прив'язки, якщо це не потрібне, вся ця функціональність сьогодні акуратно доступна за допомогою 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;
});
};
Це коротка асинхронна функція, яку потрібно використовувати, не вимагаючи сторонніх ліфтів
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 з бібліотеки синіх птахів .
Promise.each(
Iterable<any>|Promise<Iterable<any>> input,
function(any item, int index, int length) iterator
) -> Promise
Цей метод повторює масив або обіцянку масиву, який містить обіцянки (або поєднання обіцянок та значень) із заданою функцією ітератора з підписом (значення, індекс, довжина), де значенням є розв'язане значення а відповідна обіцянка у вхідному масиві. Ітерація відбувається серійно. Якщо функція ітератора повертає обіцянку або відверто, результат очікування очікується, перш ніж продовжувати наступну ітерацію. Якщо будь-яка обіцянка у вхідному масиві відхилена, то повернута обіцянка також буде відхилена.
Якщо всі ітерації успішно вирішені , 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");
});