Wywoływanie funkcji asynchronicznych / oczekujących równolegle
Z tego co rozumiem, w ES7/ES2016 umieszczanie wielu await
w kodzie będzie działać podobnie do łączenia .then()
z obietnicami, co oznacza, że będą one wykonywane jeden po drugim, a nie równolegle. Tak więc, na przykład, mamy ten kod:
await someCall();
await anotherCall();
Czy dobrze rozumiem, że anotherCall()
będzie wywołane tylko wtedy, gdy someCall()
zostanie zakończone? Jaki jest najbardziej elegancki sposób nazywania ich równolegle?
Chcę go użyć w Node, więc może jest rozwiązanie z async biblioteka?
EDIT: nie jestem zadowolony z rozwiązania podanego w tym pytaniu: spowolnienie z powodu nie-równoległego oczekiwania na obietnice w generatorach asynchronicznych , ponieważ używa generatorów i pytam o bardziej ogólny przypadek użycia.
10 answers
Możesz czekać na Promise.all()
:
await Promise.all([someCall(), anotherCall()]);
Aby zapisać wyniki:
let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
Zauważ, że Promise.all
szybko się nie powiedzie, co oznacza, że jak tylko jedna z obietnic mu dostarczona odrzuci, to cała rzecz odrzuci.
const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))
Promise.all([happy('happy', 100), sad('sad', 50)])
.then(console.log).catch(console.log) // 'sad'
Jeśli zamiast tego chcesz poczekać na spełnienie lub odrzucenie wszystkich obietnic, możesz użyć Promise.allSettled
. Należy pamiętać, że Internet Explorer nie obsługuje natywnie tego metoda.
const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))
Promise.allSettled([happy('happy', 100), sad('sad', 50)])
.then(console.log) // [{ "status":"fulfilled", "value":"happy" }, { "status":"rejected", "reason":"sad" }]
Uwaga: jeśli użyjesz
Promise.all
akcji, które udało się zakończyć przed odrzuceniem, nie zostaną wycofane, więc być może będziesz musiał zająć się taką sytuacją. Na przykład jeśli masz 5 akcji, 4 szybkie, 1 powolne i powolne odrzucenia. Te 4 akcje mogą być już wykonane, więc może być konieczne cofnięcie. W takiej sytuacji rozważ użyciePromise.allSettled
, podczas gdy dostarczy ona dokładnych szczegółów, które działanie zakończyło się niepowodzeniem, a które nie.
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
2020-12-08 20:16:37
TL; DR
Użyj Promise.all
dla równoległych wywołań funkcji, zachowanie odpowiedzi nie jest prawidłowe, gdy wystąpi błąd.
Najpierw wykonaj Wszystkie asynchroniczne wywołania naraz i uzyskaj wszystkie Promise
obiekty. Po drugie, użyj await
na obiektach Promise
. W ten sposób, podczas oczekiwania na pierwsze Promise
, aby rozwiązać inne asynchroniczne wywołania, nadal postępują. Ogólnie rzecz biorąc, będziesz czekać tylko tak długo, jak najwolniejsze połączenie asynchroniczne. Na przykład:
// Begin first call and store promise without waiting
const someResult = someCall();
// Begin second call and store promise without waiting
const anotherResult = anotherCall();
// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];
// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise
Jsbin przykład: http://jsbin.com/xerifanima/edit?js, konsola
Zastrzeżenie: nie ma znaczenia, czy wywołania await
są na tej samej linii, czy na różnych liniach, o ile pierwsze wywołanie await
nastąpi po wszystkich wywołaniach asynchronicznych. Zobacz komentarz Johnnyhka.
Update: ta odpowiedź ma inny czas obsługi błędów zgodnie z odpowiedzią @bergi, robi a nie rzucać out błąd jak błąd występuje, ale po wszystkie obietnice są wykonywane.
Porównuję wynik z końcówką @ jonny: [result1, result2] = Promise.all([async1(), async2()])
, sprawdź poniższy fragment kodu
const correctAsync500ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, 'correct500msResult');
});
};
const correctAsync100ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 100, 'correct100msResult');
});
};
const rejectAsync100ms = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, 'reject100msError');
});
};
const asyncInArray = async (fun1, fun2) => {
const label = 'test async functions in array';
try {
console.time(label);
const p1 = fun1();
const p2 = fun2();
const result = [await p1, await p2];
console.timeEnd(label);
} catch (e) {
console.error('error is', e);
console.timeEnd(label);
}
};
const asyncInPromiseAll = async (fun1, fun2) => {
const label = 'test async functions with Promise.all';
try {
console.time(label);
let [value1, value2] = await Promise.all([fun1(), fun2()]);
console.timeEnd(label);
} catch (e) {
console.error('error is', e);
console.timeEnd(label);
}
};
(async () => {
console.group('async functions without error');
console.log('async functions without error: start')
await asyncInArray(correctAsync500ms, correctAsync100ms);
await asyncInPromiseAll(correctAsync500ms, correctAsync100ms);
console.groupEnd();
console.group('async functions with error');
console.log('async functions with error: start')
await asyncInArray(correctAsync500ms, rejectAsync100ms);
await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms);
console.groupEnd();
})();
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
2019-01-21 20:36:34
Update:
Oryginalna odpowiedź utrudnia (a w niektórych przypadkach uniemożliwia) poprawną obsługę odrzuceń obietnic. Poprawnym rozwiązaniem jest użycie Promise.all
:
const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
Oryginalna odpowiedź:
Upewnij się, że wywołujesz obie funkcje, zanim poczekasz na któreś z nich:
// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();
// Await both promises
const someResult = await somePromise;
const anotherResult = await anotherPromise;
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
2019-02-05 22:01:06
Jest inny sposób bez obietnicy.wszystkie (), aby zrobić to równolegle:
Najpierw mamy 2 funkcje do drukowania liczb:
function printNumber1() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number1 is done");
resolve(10);
},1000);
});
}
function printNumber2() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number2 is done");
resolve(20);
},500);
});
}
To jest sekwencyjne:
async function oneByOne() {
const number1 = await printNumber1();
const number2 = await printNumber2();
}
//Output: Number1 is done, Number2 is done
To jest równoległe:
async function inParallel() {
const promise1 = printNumber1();
const promise2 = printNumber2();
const number1 = await promise1;
const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done
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
2019-02-27 13:25:17
Stworzyłem gist testujący różne sposoby rozwiązywania obietnic, z wynikami. Pomocne może być zapoznanie się z opcjami, które działają.
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-25 07:06:26
W moim przypadku mam kilka zadań, które chcę wykonać równolegle, ale muszę zrobić coś innego z wynikiem tych zadań.
function wait(ms, data) {
console.log('Starting task:', data, ms);
return new Promise(resolve => setTimeout(resolve, ms, data));
}
var tasks = [
async () => {
var result = await wait(1000, 'moose');
// do something with result
console.log(result);
},
async () => {
var result = await wait(500, 'taco');
// do something with result
console.log(result);
},
async () => {
var result = await wait(5000, 'burp');
// do something with result
console.log(result);
}
]
await Promise.all(tasks.map(p => p()));
console.log('done');
I Wyjście:
Starting task: moose 1000
Starting task: taco 500
Starting task: burp 5000
taco
moose
burp
done
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
2019-10-07 14:37:28
Czekam Na Obietnicę.all ([someCall (), anotherCall ()]); jak już wspomniano, będzie działać jako ogrodzenie wątku( bardzo często w kodzie równoległym jako CUDA), stąd pozwoli wszystkie obietnice w nim działać bez blokowania siebie, ale uniemożliwi kontynuowanie wykonywania, dopóki wszystkie nie zostaną rozwiązane.
Innym podejściem, które warto podzielić jest węzeł.js async, który pozwoli również łatwo kontrolować ilość współbieżności, która jest zwykle pożądana, jeśli zadanie jest bezpośrednio związane z wykorzystaniem ograniczone zasoby jak wywołanie API, operacje We/Wy itp.
// create a queue object with concurrency 2
var q = async.queue(function(task, callback) {
console.log('Hello ' + task.name);
callback();
}, 2);
// assign a callback
q.drain = function() {
console.log('All items have been processed');
};
// add some items to the queue
q.push({name: 'foo'}, function(err) {
console.log('Finished processing foo');
});
q.push({name: 'bar'}, function (err) {
console.log('Finished processing bar');
});
// add some items to the queue (batch-wise)
q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) {
console.log('Finished processing item');
});
// add some items to the front of the queue
q.unshift({name: 'bar'}, function (err) {
console.log('Finished processing bar');
});
Podziękowania dla autora artykułu (Czytaj więcej )
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
2020-05-18 00:34:47
// A generic test function that can be configured
// with an arbitrary delay and to either resolve or reject
const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => {
console.log(`Done ${ delay }`);
resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`)
}, delay));
// Our async handler function
const handler = async () => {
// Promise 1 runs first, but resolves last
const p1 = test(10000, true);
// Promise 2 run second, and also resolves
const p2 = test(5000, true);
// Promise 3 runs last, but completes first (with a rejection)
// Note the catch to trap the error immediately
const p3 = test(1000, false).catch(e => console.log(e));
// Await all in parallel
const r = await Promise.all([p1, p2, p3]);
// Display the results
console.log(r);
};
// Run the handler
handler();
/*
Done 1000
Reject 1000
Done 5000
Done 10000
*/
Podczas gdy ustawienie p1, p2 i p3 nie jest ściśle uruchamiane równolegle, nie zatrzymują one żadnej realizacji i można przechwycić błędy kontekstowe za pomocą haczyka.
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
2019-07-03 20:10:32
Tworzę funkcję pomocniczą waitAll, może to sprawi, że będzie słodsza. Na razie działa tylko w nodejs, nie w przeglądarce chrome.
//const parallel = async (...items) => {
const waitAll = async (...items) => {
//this function does start execution the functions
//the execution has been started before running this code here
//instead it collects of the result of execution of the functions
const temp = [];
for (const item of items) {
//this is not
//temp.push(await item())
//it does wait for the result in series (not in parallel), but
//it doesn't affect the parallel execution of those functions
//because they haven started earlier
temp.push(await item);
}
return temp;
};
//the async functions are executed in parallel before passed
//in the waitAll function
//const finalResult = await waitAll(someResult(), anotherResult());
//const finalResult = await parallel(someResult(), anotherResult());
//or
const [result1, result2] = await waitAll(someResult(), anotherResult());
//const [result1, result2] = await parallel(someResult(), anotherResult());
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
2019-02-21 00:50:51
Głosuję za:
await Promise.all([someCall(), anotherCall()]);
Bądź świadomy momentu wywołania funkcji, może to spowodować nieoczekiwany rezultat:
// Supposing anotherCall() will trigger a request to create a new User
if (callFirst) {
await someCall();
} else {
await Promise.all([someCall(), anotherCall()]); // --> create new User here
}
Ale podążanie zawsze wywołuje żądanie utworzenia nowego Użytkownika
// Supposing anotherCall() will trigger a request to create a new User
const someResult = someCall();
const anotherResult = anotherCall(); // ->> This always creates new User
if (callFirst) {
await someCall();
} else {
const finalResult = [await someResult, await anotherResult]
}
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-09-28 09:59:51