비동기/대기 함수를 병렬로 호출하다
에 여러 개의 ES7/ES2016을 것으로.await
동작합니다..then()
약속을 하는 것, 즉 병행하는 것이 아니라 차례로 실행한다는 것을 의미합니다.예를 들어 다음과 같은 코드가 있습니다.
await someCall();
await anotherCall();
한 것이 ?anotherCall()
때는 불리다someCall()
완되????이들을 병렬로 부르는 가장 우아한 방법은 무엇일까요?
노드에서 사용하고 싶은데 비동기 라이브러리가 있는 솔루션이 있을까요?
편집: 이 질문에 제시된 솔루션에 만족하지 않습니다.비동기 발전기에서 약속을 기다리는 비병렬로 인한 속도 저하. 발전기를 사용하기 때문에 더 일반적인 사용 사례에 대해 묻고 있습니다.
됩니다.Promise.all()
:
await Promise.all([someCall(), anotherCall()]);
결과를 저장하려면:
let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
:Promise.all
즉, 제공된 약속 중 하나가 거부되면 모든 것이 거부됩니다.
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'
대신 모든 약속이 이행되거나 거부될 때까지 기다릴 경우 를 사용할 수 있습니다.Internet Explorer는 기본적으로 이 방법을 지원하지 않습니다.
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" }]
주의: 를 사용하는 경우
Promise.all
거부되기 전에 완료되는 액션은 롤백되지 않으므로 이러한 상황에 대처해야 할 수 있습니다.예를 들어 빠른 동작 4개, 느린 동작 1개, 느린 동작 5개가 있습니다.이 4가지 작업은 이미 실행되었을 수 있으므로 롤백해야 할 수 있습니다.에서는, 「」의 사용을 해 주세요.Promise.allSettled
어떤 작업이 실패했는지, 어떤 작업이 실패하지 않았는지 정확하게 설명합니다.
TL;DR
Promise.all
병렬 함수 호출의 경우 오류 발생 시 응답 동작이 올바르지 않습니다.
먼저 모든 비동기 콜을 동시에 실행하여 모든 비동기 콜을 취득합니다.Promise
,,. 두 째,, 을 사용하세요.await
Promise
첫 번째를 이렇게 하면 됩니다.Promise
을 사용하다전체적으로 볼 때 가장 느린 비동기 콜만큼만 대기합니다.예를 들어 다음과 같습니다.
// 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 예:http://jsbin.com/xerifanima/edit?js,console
경고: 이 경우,await
다른 회선상에 단, 첫 번째 ""는 ""이다", ""는 ""이다.await
콜은 모든 비동기 콜 후에 발생합니다.쟈니 보기HK 댓글.
업데이트: 이 답변은 @bergi의 답변에 따라 오류 처리 타이밍이 다릅니다.오류가 발생했을 때 에러가 발생하지 않고 모든 약속이 실행된 후에 에러가 처리됩니다.결과를 @jonny의 힌트와 비교합니다.[result1, result2] = Promise.all([async1(), async2()])
해 주세요.
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();
})();
업데이트:
원래 답변에서는 약속 거부를 올바르게 처리하기가 어렵습니다(경우에 따라서는 불가능). 방법은 '이렇게'를 하는 것입니다.Promise.all
:
const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
원답:
다음 중 하나를 대기하기 전에 두 함수를 호출해야 합니다.
// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();
// Await both promises
const someResult = await somePromise;
const anotherResult = await anotherPromise;
Promise.all()을 사용하지 않고 병렬로 실행할 수 있는 다른 방법이 있습니다.
우선, 다음의 2개의 번호 인쇄 기능이 있습니다.
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);
});
}
다음은 시퀀셜입니다.
async function oneByOne() {
const number1 = await printNumber1();
const number2 = await printNumber2();
}
//Output: Number1 is done, Number2 is done
이것은 병렬입니다.
async function inParallel() {
const promise1 = printNumber1();
const promise2 = printNumber2();
const number1 = await promise1;
const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done
약속을 해결하기 위한 여러 가지 방법을 테스트하고 결과를 내도록 했습니다.유효한 옵션을 확인하는 것이 도움이 될 수 있습니다.
// Simple gist to test parallel promise resolution when using async / await
function promiseWait(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, time);
});
}
async function test() {
return [
await promiseWait(1000),
await promiseWait(5000),
await promiseWait(9000),
await promiseWait(3000),
]
}
async function test2() {
return {
'aa': await promiseWait(1000),
'bb': await promiseWait(5000),
'cc': await promiseWait(9000),
'dd': await promiseWait(3000),
}
}
async function test3() {
return await {
'aa': promiseWait(1000),
'bb': promiseWait(5000),
'cc': promiseWait(9000),
'dd': promiseWait(3000),
}
}
async function test4() {
const p1 = promiseWait(1000);
const p2 = promiseWait(5000);
const p3 = promiseWait(9000);
const p4 = promiseWait(3000);
return {
'aa': await p1,
'bb': await p2,
'cc': await p3,
'dd': await p4,
};
}
async function test5() {
return await Promise.all([
await promiseWait(1000),
await promiseWait(5000),
await promiseWait(9000),
await promiseWait(3000),
]);
}
async function test6() {
return await Promise.all([
promiseWait(1000),
promiseWait(5000),
promiseWait(9000),
promiseWait(3000),
]);
}
async function test7() {
const p1 = promiseWait(1000);
const p2 = promiseWait(5000);
const p3 = promiseWait(9000);
return {
'aa': await p1,
'bb': await p2,
'cc': await p3,
'dd': await promiseWait(3000),
};
}
let start = Date.now();
test().then((res) => {
console.log('Test Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test2().then((res) => {
console.log('Test2 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test3().then((res) => {
console.log('Test3 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test4().then((res) => {
console.log('Test4 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test5().then((res) => {
console.log('Test5 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test6().then((res) => {
console.log('Test6 Done, elapsed', (Date.now() - start) / 1000, res);
});
start = Date.now();
test7().then((res) => {
console.log('Test7 Done, elapsed', (Date.now() - start) / 1000, res);
});
});
});
});
});
});
/*
Test Done, elapsed 18.006 [ true, true, true, true ]
Test2 Done, elapsed 18.009 { aa: true, bb: true, cc: true, dd: true }
Test3 Done, elapsed 0 { aa: Promise { <pending> },
bb: Promise { <pending> },
cc: Promise { <pending> },
dd: Promise { <pending> } }
Test4 Done, elapsed 9 { aa: true, bb: true, cc: true, dd: true }
Test5 Done, elapsed 18.008 [ true, true, true, true ]
Test6 Done, elapsed 9.003 [ true, true, true, true ]
Test7 Done, elapsed 12.007 { aa: true, bb: true, cc: true, dd: true }
*/
저 같은 경우에는 병행해서 실행하고 싶은 작업이 몇 가지 있는데, 그 결과와는 다른 작업을 해야 합니다.
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');
그리고 출력:
Starting task: moose 1000
Starting task: taco 500
Starting task: burp 5000
taco
moose
burp
done
(async function(){
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');
})();
wait Promise.all([someCall()], anotherCall()); 이미 언급한 바와 같이 스레드펜스(CUDA로서 매우 일반적인 코드)로서 기능하기 때문에 그 안에 있는 모든 약속이 서로 차단되지 않고 실행되지만 ALL이 해결될 때까지 실행은 계속되지 않습니다.
공유할 가치가 있는 또 다른 접근방식은 Node.js 비동기입니다.이 접근방식은 API 호출, I/O 조작 등 한정된 자원의 사용과 직접 연계되어 있는 경우에 일반적으로 바람직한 동시성의 양을 쉽게 제어할 수 있도록 합니다.
// 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');
});
중간 기사 자동에 대한 크레딧(자세한 내용은 읽기)
// 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
*/
p1, p2 및 p3 설정은 엄밀하게 병렬로 실행되지는 않지만 실행이 지연되지 않으므로 캐치를 사용하여 컨텍스트에러를 트랩 할 수 있습니다.
대기하지 않고 여러 비동기 함수를 호출할 수 있습니다.이렇게 하면 병렬로 실행됩니다.그 동안 반환된 약속을 변수에 저장하고 어느 시점에서 개별적으로 또는 Promise.all()을 사용하여 대기하여 결과를 처리합니다.
함수 콜을 try...catch로 랩하여 개별 비동기 액션의 실패를 처리하고 폴백로직을 제공할 수도 있습니다.
예를 들어 로그는 개별 비동기 함수의 실행 시작 시에 인쇄된 로그가 첫 번째 함수가 해결되는 데 5초가 걸리더라도 즉시 인쇄됩니다.
function someLongFunc () {
return new Promise((resolve, reject)=> {
console.log('Executing function 1')
setTimeout(resolve, 5000)
})
}
function anotherLongFunc () {
return new Promise((resolve, reject)=> {
console.log('Executing function 2')
setTimeout(resolve, 5000)
})
}
async function main () {
let someLongFuncPromise, anotherLongFuncPromise
const start = Date.now()
try {
someLongFuncPromise = someLongFunc()
}
catch (ex) {
console.error('something went wrong during func 1')
}
try {
anotherLongFuncPromise = anotherLongFunc()
}
catch (ex) {
console.error('something went wrong during func 2')
}
await someLongFuncPromise
await anotherLongFuncPromise
const totalTime = Date.now() - start
console.log('Execution completed in ', totalTime)
}
main()
이것은 Promise로 달성할 수 있습니다.allSettled()는 Promise.all()과 비슷하지만 fail-fast 동작은 없습니다.
async function Promise1() {
throw "Failure!";
}
async function Promise2() {
return "Success!";
}
const [Promise1Result, Promise2Result] = await Promise.allSettled([Promise1(), Promise2()]);
console.log(Promise1Result); // {status: "rejected", reason: "Failure!"}
console.log(Promise2Result); // {status: "fulfilled", value: "Success!"}
주의: 브라우저 지원이 제한된 블리딩 엣지 기능이기 때문에 이 기능에는 폴리필을 포함시키는 것을 강력히 권장합니다.
도우미 기능 waitAll을 만듭니다만, 더 달콤하게 할 수 있을지도 모릅니다.현재 nodejs에서만 작동하며 브라우저 크롬에서는 작동하지 않습니다.
//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());
투표 대상:
await Promise.all([someCall(), anotherCall()]);
함수를 호출하는 순간 예기치 않은 결과가 발생할 수 있습니다.
// 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
}
단, 항상 팔로우하면 새로운 사용자 생성 요청이 트리거됩니다.
// 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]
}
언급URL : https://stackoverflow.com/questions/35612428/call-async-await-functions-in-parallel
'programing' 카테고리의 다른 글
앵귤러 루트와 앵귤러 라우터의 차이점은 무엇입니까? (0) | 2022.09.27 |
---|---|
PHP에서 첫 요일을 얻으시겠습니까? (0) | 2022.09.27 |
소켓 '/var/lib/mysql/mysql'을 통해 로컬 MySQL 서버에 연결할 수 없습니다.양말' (2) (0) | 2022.09.26 |
PHP에서의 FOR vs FOREACH의 퍼포먼스 (0) | 2022.09.26 |
MySQL을 사용한 3개의 테이블 결합 (0) | 2022.09.26 |