JavaScript, Node.JS: jest Array.forEach asynchronous?

Mam pytanie dotyczące natywnej Array.forEach implementacji JavaScript: czy zachowuje się on asynchronicznie? Na przykład, jeśli wywołam:

[many many elements].forEach(function () {lots of work to do})
Czy to nie będzie blokowanie?
Author: Yves M., 2011-02-19

10 answers

Nie, blokuje. Spójrz na specyfikację algorytmu .

Jednak może łatwiej zrozumieć implementację podano na 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);
    }
  };
}

Jeśli musisz wykonać dużo kodu dla każdego elementu, powinieneś rozważyć użycie innego podejścia:

function processArray(items, process) {
    var todo = items.concat();

    setTimeout(function() {
        process(todo.shift());
        if(todo.length > 0) {
            setTimeout(arguments.callee, 25);
        }
    }, 25);
}

A następnie wywołaj przez:

processArray([many many elements], function () {lots of work to do});

To byłoby nieblokujące wtedy. Przykład jest wzięty z wysoka Wydajność JavaScript.

Inny opcja może być web workers.

 318
Author: Felix Kling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-12 15:30:24

Jeśli potrzebujesz asynchronicznej wersji Array.forEach i podobne, są dostępne w węźle.moduł js "async": http://github.com/caolan/async ... jako bonus ten moduł działa również w przeglądarce.

async.each(openFiles, saveFile, function(err){
    // if any of the saves produced an error, err would equal that error
});
 68
Author: Caolan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-08 07:19:21

Istnieje wspólny wzorzec wykonywania naprawdę ciężkich obliczeń w węźle, który może mieć zastosowanie do ciebie...

Węzeł jest jednowątkowy (jako celowy wybór projektu, zobacz Co To jest węzeł.js?); oznacza to, że może używać tylko jednego rdzenia. Nowoczesne pudełka mają 8, 16 lub nawet więcej rdzeni, więc może to pozostawić 90+% maszyny bezczynnej. Wspólny wzorzec dla usługi REST polega na odpaleniu jednego procesu węzła na rdzeń i umieszczeniu go za lokalnym load balancerem, takim jak http://nginx.org/.

rozwidlenie dziecka - Za to, co próbujesz zrobić, istnieje inny wspólny wzór, rozwidlenie procesu dziecka, aby wykonać ciężkie podnoszenie. Plusem jest to, że proces potomny może wykonywać ciężkie obliczenia w tle, podczas gdy proces rodzica reaguje na inne zdarzenia. Haczyk polega na tym, że nie możesz / nie powinieneś dzielić pamięci z tym procesem potomnym (nie bez wielu konturów i trochę kodu natywnego); musisz przejść wiadomości. Będzie to działać pięknie, jeśli rozmiar danych wejściowych i wyjściowych jest mały w porównaniu do obliczeń, które należy wykonać. Możesz nawet odpalić węzeł dziecka.js przetwarza i używa tego samego kodu, którego używałeś wcześniej.

Na przykład:

var child_process = require('child_process');
function run_in_child(array, cb) {
    var process = child_process.exec('node libfn.js', function(err, stdout, stderr) {
        var output = JSON.parse(stdout);
        cb(err, output);
    });
    process.stdin.write(JSON.stringify(array), 'utf8');
    process.stdin.end();
}
 15
Author: Dave Dopson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:50

Array.forEach jest przeznaczony do przetwarzania rzeczy nie czekających, a nie ma nic do zyskania dokonując obliczeń asynchronicznych w pętli zdarzeń (webworkerzy dodają multiprocessing, jeśli potrzebujesz obliczeń wielordzeniowych). Jeśli chcesz poczekać na zakończenie wielu zadań, użyj licznika, który możesz zawinąć w klasę semafora.

 4
Author: Tobu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-25 00:01:03

Edytuj 2018-10-11: Wygląda na to, że istnieje duża szansa, że opisany poniżej standard może nie przejść, rozważ pipelining jako alternatywę(nie zachowuje się dokładnie tak samo, ale metody mogą być zaimplementowane w podobnym manor).

Właśnie dlatego jestem podekscytowany es7, w przyszłości będziesz mógł zrobić coś takiego jak poniższy kod(niektóre specyfikacje nie są kompletne, więc używaj ostrożnie, postaram się być na bieżąco). Ale w zasadzie używając nowego:: bind operator, będziesz mógł uruchomić metodę na obiekcie tak, jakby prototyp obiektu zawierał metodę. np [Object]:: [Method] gdzie normalnie można wywołać [Object].[ObjectsMethod]

Uwaga Aby zrobić to dzisiaj (24-July-16) i aby działało we wszystkich przeglądarkach, musisz transpile kodu dla następującej funkcjonalności: Import / Export, Funkcje strzałek, obietnice, Async / wait i co najważniejsze funkcja bind . Poniższy kod można modyfikować, aby używać tylko funkcji bind, jeśli nessesary, cała ta funkcjonalność jest obecnie dostępna za pomocą babel .

YourCode.js (gdzie 'lots of work to do' musi po prostu zwrócić obietnicę, rozwiązując ją, gdy praca asynchroniczna jest wykonywana.)

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;
    });
};
 3
Author: Josh Mc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-11 01:06:49

Jest to krótka funkcja asynchroniczna do użycia bez konieczności korzystania z bibliotek stron trzecich

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);
};
 1
Author: Rax Wunter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-21 20:18:27

Istnieje pakiet na npm dla easy asynchroniczny dla każdej pętli .

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');
});

Również inna odmiana forAllAsync

 0
Author: Philip Kirkbride,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-21 14:35:26

Możliwe jest zakodowanie nawet takiego rozwiązania na przykład:

 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);
});

Z drugiej strony, jest znacznie wolniejszy niż"dla".

W przeciwnym razie doskonała Biblioteka asynchroniczna może to zrobić: https://caolan.github.io/async/docs.html#each

 0
Author: signo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-26 22:41:35

Oto mały przykład, który możesz uruchomić, aby go przetestować:

[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);
});

Wytworzy coś takiego (jeśli zajmie to zbyt mniej/dużo czasu, zwiększ / zmniejsz liczbę iteracji):

(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
 0
Author: adiian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-17 14:56:13

Użyj Obietnicy.każda z } biblioteka bluebird .

Promise.each(
Iterable<any>|Promise<Iterable<any>> input,
function(any item, int index, int length) iterator
) -> Promise

Metoda ta iteruje nad tablicą lub obietnicą tablicy, która zawiera obietnice(lub mieszankę obietnic i wartości) z podaną funkcją iterator z podpisem (wartość, indeks, długość) Gdzie wartość jest rozwiązaną wartością odpowiedniej obietnicy w tablicy wejściowej. iteracja dzieje się seryjnie. Jeśli funkcja iteratora zwraca obietnicę lub thenable, to wynik obietnicy jest oczekiwany przed kontynuacją kolejnej iteracji. Jeśli jakakolwiek obietnica w tablicy wejściowej zostanie odrzucona, wtedy również zostanie odrzucona zwrócona obietnica.

Jeśli wszystkie iteracje zakończą się pomyślnie, Obiecaj.każdy rozwiązuje oryginalną tablicę niezmodyfikowaną . Jeśli jednak jedna iteracja odrzuci lub popełni błąd, Obiecaj.każda natychmiast przerywa wykonywanie i nie przetwarza kolejnych iteracji. W tym przypadku zwracana jest wartość błąd lub odrzucona zamiast oryginalnej tablicy.

Ta metoda jest przeznaczona do stosowania w przypadku skutków ubocznych.

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");
});
 0
Author: Igor Litvinovich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-06 08:56:53