Tengo una pregunta sobre la Array.forEach
implementación nativa de JavaScript: ¿se comporta de forma asincrónica? Por ejemplo, si llamo:
[many many elements].forEach(function () {lots of work to do})
¿Será esto sin bloqueo?
Tengo una pregunta sobre la Array.forEach
implementación nativa de JavaScript: ¿se comporta de forma asincrónica? Por ejemplo, si llamo:
[many many elements].forEach(function () {lots of work to do})
¿Será esto sin bloqueo?
Respuestas:
No, está bloqueando. Echa un vistazo a la especificación del algoritmo .
Sin embargo, una implementación quizás más fácil de entender se da en 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);
}
};
}
Si tiene que ejecutar una gran cantidad de código para cada elemento, debe considerar usar un enfoque diferente:
function processArray(items, process) {
var todo = items.concat();
setTimeout(function() {
process(todo.shift());
if(todo.length > 0) {
setTimeout(arguments.callee, 25);
}
}, 25);
}
y luego llamarlo con:
processArray([many many elements], function () {lots of work to do});
Esto sería sin bloqueo entonces. El ejemplo está tomado de JavaScript de alto rendimiento .
Otra opción podrían ser los trabajadores web .
forEach
no no bloquear en await
declaraciones por ejemplo, y usted debe más bien utilizar un for
bucle: stackoverflow.com/questions/37962880/...
await
internas async
. Pero forEach
no sabe qué son las funciones asíncronas. Tenga en cuenta que las funciones asíncronas son solo funciones que devuelven una promesa. ¿Esperaría forEach
manejar una promesa devuelta de la devolución de llamada? forEach
ignora completamente el valor de retorno de la devolución de llamada. Solo podría manejar una devolución de llamada asíncrona si fuera asíncrona.
Si necesita una versión asíncrona Array.forEach
y similar, están disponibles en el módulo 'async' de Node.js: http://github.com/caolan/async ... como beneficio adicional, este módulo también funciona en el navegador .
async.each(openFiles, saveFile, function(err){
// if any of the saves produced an error, err would equal that error
});
eachSeries
en su lugar.
Hay un patrón común para hacer un cálculo realmente pesado en Node que puede ser aplicable a usted ...
El nodo es de un solo subproceso (como una opción de diseño deliberada, consulte ¿Qué es Node.js? ); Esto significa que solo puede utilizar un solo núcleo. Las cajas modernas tienen 8, 16 o incluso más núcleos, por lo que esto podría dejar el 90% de la máquina inactiva. El patrón común para un servicio REST es iniciar un proceso de nodo por núcleo y colocarlos detrás de un equilibrador de carga local como http://nginx.org/ .
Bifurcar a un niño : para lo que está tratando de hacer, hay otro patrón común, bifurcar un proceso de niño para hacer el trabajo pesado. Lo bueno es que el proceso secundario puede hacer cálculos pesados en segundo plano mientras que el proceso principal responde a otros eventos. El problema es que no puede / no debe compartir memoria con este proceso secundario (no sin MUCHAS contorsiones y algún código nativo); Tienes que pasar mensajes. Esto funcionará maravillosamente si el tamaño de sus datos de entrada y salida es pequeño en comparación con el cálculo que debe realizarse. Incluso puede iniciar un proceso hijo de node.js y usar el mismo código que estaba usando anteriormente.
Por ejemplo:
var child_process = require ('child_process'); función run_in_child (array, cb) { var process = child_process.exec ('node libfn.js', function (err, stdout, stderr) { salida var = JSON.parse (stdout); cb (err, salida); }); process.stdin.write (JSON.stringify (array), 'utf8'); process.stdin.end (); }
Array.forEach
está destinado a calcular cosas que no esperan, y no se gana nada haciendo que los cálculos sean asíncronos en un bucle de eventos (los trabajadores web agregan multiprocesamiento, si necesita cómputo multinúcleo). Si desea esperar a que finalicen varias tareas, use un contador, que puede ajustar en una clase de semáforo.
Editar 2018-10-11: Parece que hay una buena posibilidad de que el estándar descrito a continuación no se cumpla, considere la canalización como una alternativa (no se comporta exactamente igual pero los métodos podrían implementarse de manera similar).
Esta es exactamente la razón por la que estoy entusiasmado con es7, en el futuro podrá hacer algo como el código a continuación (algunas de las especificaciones no están completas, así que úselas con precaución, intentaré mantener esto actualizado). Pero, básicamente, utilizando el nuevo operador :: bind, podrá ejecutar un método en un objeto como si el prototipo del objeto contiene el método. por ejemplo, [Object] :: [Method] donde normalmente llamarías a [Object]. [ObjectsMethod]
Tenga en cuenta que para hacer esto hoy (24 de julio-16) y que funcione en todos los navegadores, necesitará transpilar su código para la siguiente funcionalidad: Importar / Exportar , Funciones de flecha , Promesas , Asíncrono / Esperar y lo más importante , enlace de funciones . El siguiente código podría modificarse para usar solo la función bind si es necesario, toda esta funcionalidad está perfectamente disponible hoy en día usando babel .
YourCode.js (donde ' mucho trabajo por hacer ' simplemente debe devolver una promesa, resolviéndola cuando se realiza el trabajo asincrónico).
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;
});
};
Esta es una función asíncrona corta para usar sin requerir libs de terceros
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);
};
Hay un paquete en npm para asincrónico fácil para cada bucle .
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');
});
También otra variación para AllAsync
Es posible codificar incluso la solución como esta, por ejemplo:
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);
});
Por otro lado, es mucho más lento que un "para".
De lo contrario, la excelente biblioteca Async puede hacer esto: https://caolan.github.io/async/docs.html#each
Aquí hay un pequeño ejemplo que puede ejecutar para probarlo:
[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);
});
Producirá algo como esto (si toma mucho menos / mucho tiempo, aumenta / disminuye el número de iteraciones):
(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
Use Promise.each of bluebird library.
Promise.each(
Iterable<any>|Promise<Iterable<any>> input,
function(any item, int index, int length) iterator
) -> Promise
Este método itera sobre una matriz, o una promesa de una matriz, que contiene promesas (o una combinación de promesas y valores) con la función iteradora dada con la firma (valor, índice, longitud) donde el valor es el valor resuelto de un promesa respectiva en la matriz de entrada. La iteración ocurre en serie.Si la función de iterador devuelve una promesa o un thenable, entonces se espera el resultado de la promesa antes de continuar con la próxima iteración. Si se rechaza cualquier promesa en la matriz de entrada, también se rechaza la promesa devuelta.
Si todas las iteraciones se resuelven correctamente, Promise.each se resuelve en la matriz original sin modificaciones . Sin embargo, si una iteración rechaza o contiene errores, Promise.each deja de ejecutarse inmediatamente y no procesa más iteraciones. El error o el valor rechazado se devuelve en este caso en lugar de la matriz original.
Este método está destinado a ser utilizado para efectos secundarios.
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");
});