J'ai une question concernant l' Array.forEach
implémentation native de JavaScript: se comporte-t-il de manière asynchrone? Par exemple, si j'appelle:
[many many elements].forEach(function () {lots of work to do})
Sera-ce non bloquant?
J'ai une question concernant l' Array.forEach
implémentation native de JavaScript: se comporte-t-il de manière asynchrone? Par exemple, si j'appelle:
[many many elements].forEach(function () {lots of work to do})
Sera-ce non bloquant?
Réponses:
Non, c'est bloquant. Jetez un oeil à la spécification de l'algorithme .
Cependant, une implémentation peut-être plus facile à comprendre est donnée sur 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 vous devez exécuter beaucoup de code pour chaque élément, vous devriez envisager d'utiliser une approche différente:
function processArray(items, process) {
var todo = items.concat();
setTimeout(function() {
process(todo.shift());
if(todo.length > 0) {
setTimeout(arguments.callee, 25);
}
}, 25);
}
puis appelez-le avec:
processArray([many many elements], function () {lots of work to do});
Ce serait alors non bloquant. L'exemple est tiré de JavaScript haute performance .
Une autre option pourrait être les travailleurs Web .
forEach
par exemple, ne bloque pas les await
instructions et vous devriez plutôt utiliser une for
boucle: stackoverflow.com/questions/37962880/…
await
internes async
. Mais forEach
ne sait pas ce que sont les fonctions asynchrones. Gardez à l'esprit que les fonctions asynchrones ne sont que des fonctions renvoyant une promesse. Vous attendriez-vous forEach
à gérer une promesse renvoyée par le rappel? forEach
ignore complètement la valeur de retour du rappel. Il ne pourrait gérer un rappel asynchrone que s'il était lui-même asynchrone.
Si vous avez besoin d'une version conviviale Array.forEach
et similaire de asynchrone , elles sont disponibles dans le module 'async' de Node.js: http://github.com/caolan/async ... en prime, ce module fonctionne également dans le navigateur .
async.each(openFiles, saveFile, function(err){
// if any of the saves produced an error, err would equal that error
});
eachSeries
place.
Il existe un schéma commun pour effectuer un calcul très lourd dans Node qui peut vous être applicable ...
Le nœud est monothread (comme choix de conception délibéré, voir Qu'est - ce que Node.js? ); cela signifie qu'il ne peut utiliser qu'un seul cœur. Les boîtes modernes ont 8, 16 ou même plus de cœurs, ce qui pourrait laisser 90 +% de la machine inactive. Le modèle commun pour un service REST est de lancer un processus de nœud par cœur et de les placer derrière un équilibreur de charge local comme http://nginx.org/ .
Bifurquer un enfant - Pour ce que vous essayez de faire, il existe un autre schéma courant, bousculant un processus enfant pour faire le gros du travail. L'avantage est que le processus enfant peut effectuer des calculs lourds en arrière-plan tandis que votre processus parent est sensible à d'autres événements. Le hic, c'est que vous ne pouvez pas / ne devez pas partager la mémoire avec ce processus enfant (pas sans BEAUCOUP de contorsions et du code natif); vous devez passer des messages. Cela fonctionnera à merveille si la taille de vos données d'entrée et de sortie est petite par rapport au calcul qui doit être effectué. Vous pouvez même lancer un processus node.js enfant et utiliser le même code que vous utilisiez précédemment.
Par exemple:
var child_process = require ('child_process'); fonction run_in_child (tableau, cb) { var process = child_process.exec ('node libfn.js', function (err, stdout, stderr) { var sortie = JSON.parse (stdout); cb (err, sortie); }); process.stdin.write (JSON.stringify (array), 'utf8'); process.stdin.end (); }
Array.forEach
est destiné à l'informatique qui n'attend pas, et il n'y a rien à gagner à faire des calculs asynchrones dans une boucle d'événement (les webworkers ajoutent le multiprocessing, si vous avez besoin d'un calcul multi-core). Si vous souhaitez attendre la fin de plusieurs tâches, utilisez un compteur que vous pouvez encapsuler dans une classe de sémaphore.
Edit 2018-10-11: Il semble qu'il y ait de fortes chances que la norme décrite ci-dessous ne passe pas, envisagez la canalisation comme alternative (ne se comporte pas exactement de la même manière mais des méthodes pourraient être mises en œuvre dans un manoir similaire).
C'est exactement pourquoi je suis enthousiasmé par es7, à l'avenir, vous pourrez faire quelque chose comme le code ci-dessous (certaines des spécifications ne sont pas complètes, utilisez-les donc avec prudence, je vais essayer de le garder à jour). Mais en gros, en utilisant l'opérateur new :: bind, vous pourrez exécuter une méthode sur un objet comme si le prototype de l'objet contenait la méthode. par exemple [Object] :: [Method] où normalement vous appelleriez [Object]. [ObjectsMethod]
Notez que pour ce faire aujourd'hui (24-juillet-16) et que cela fonctionne dans tous les navigateurs, vous devrez transpiler votre code pour les fonctionnalités suivantes: importation / exportation , fonctions fléchées , promesses , asynchrone / attente et surtout fonction bind . Le code ci-dessous pourrait être modifié pour n'utiliser que la fonction bind si nécessaire, toutes ces fonctionnalités sont parfaitement disponibles aujourd'hui en utilisant babel .
YourCode.js (où « beaucoup de travail à faire » doit simplement renvoyer une promesse, la résoudre lorsque le travail asynchrone est terminé.)
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;
});
};
Il s'agit d'une fonction asynchrone courte à utiliser sans nécessiter de bibliothèques tierces
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);
};
Il y a un paquet sur npm pour une asynchrone facile pour chaque boucle .
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');
});
Aussi une autre variante pour AllAsync
Il est possible de coder même la solution comme celle-ci par exemple:
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);
});
En revanche, il est beaucoup plus lent qu'un "pour".
Sinon, l'excellente bibliothèque Async peut le faire: https://caolan.github.io/async/docs.html#each
Voici un petit exemple que vous pouvez exécuter pour le tester:
[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);
});
Cela produira quelque chose comme ça (si cela prend trop de temps / moins, augmentez / diminuez le nombre d'itérations):
(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
Utilisation Promise.each de Bluebird bibliothèque.
Promise.each(
Iterable<any>|Promise<Iterable<any>> input,
function(any item, int index, int length) iterator
) -> Promise
Cette méthode parcourt un tableau, ou une promesse d'un tableau, qui contient des promesses (ou un mélange de promesses et de valeurs) avec la fonction d' itérateur donnée avec la signature (valeur, index, longueur) où la valeur est la valeur résolue d'un promesse respective dans le tableau d'entrée. L'itération se produit en série. Si la fonction itérateur retourne une promesse ou un élément exploitable, le résultat de la promesse est attendu avant de continuer avec l'itération suivante. Si une promesse du tableau d'entrée est rejetée, la promesse retournée est également rejetée.
Si toutes les itérations se résolvent avec succès, Promise.each résout le tableau d'origine non modifié . Cependant, si une itération rejette ou fait une erreur, Promise.each cesse immédiatement son exécution et ne traite aucune autre itération. L'erreur ou la valeur rejetée est retournée dans ce cas au lieu du tableau d'origine.
Cette méthode est destinée à être utilisée pour les effets secondaires.
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");
});