async/await의 병행·직렬 실행을 측정하면서 이해하기-JavaScript
35197 단어 JavaScripttech
개시하다
본 보도의 목적은
async/await
문법을 사용하는 비동기 처리를 깊이 있게 이해하고 병렬, 직렬로 연결할 때 어떻게 기술하는 것이 좋을지 기술하는 것이다.주의 사항
방법체인을 사용해 간단한 기술방법을 사용할 수 있도록 기존 유형에 확장방법을 추가하는 방법을 적용한 만큼 지속 활용 여부는 사업 가이드라인에 적합한지에 달렸다.
만약 사용하지 않는 방법을 사용한다면 이전의 방법으로 한 번 묘사하는 것을 추천합니다. 읽기가 매우 힘들 것 같습니다.
전지식
인상은 전류이고 직렬 집행은 배터리의 직렬 집행이며 병렬 집행은 병렬 연결이어서 이해하기 쉽다.
직렬 실행
직렬 실행은 처리 결과가 전파되어야 할 때 주로 사용된다.
Promise의
then()
방법은 아래의 처리를 받아들여 새로운 Promise로 되돌아오기 때문에 방법 체인으로 기술할 수 있다.const p = asyncHoge()
.then(fooFromHoge) // 同期処理も可
.then(asyncPiyoFromFoo);
console.log(await p);
병렬 실행
병렬 실행은 전파가 필요 없는 처리를 동시에 실행하는 데 사용됩니다.
주로
Promise.all()
방법을 사용하는데 그 중에서 여러 과정을 포함하는iterable(배열 등)가 전달되고 await를 통해 해결된 결과를 수조로 받아들인다.const p = Promise.all([
asyncHoge(),
foo(),
asyncPiyo(),
]);
console.log(await p);
병렬 블렌드 실행
혼합 모델은 복잡한 구조로 직렬은 때로는 병렬로 나뉘고 병렬은 때로는 각 요소를 합치며 내부의 각 요소도 귀속되는 구조이다.
// コードは複雑になるため、拡張メソッドを使った方法を後述していきます。
측정 코드
코드를 쓸 때 다음과 같은 확장 방법을 실시하였다.
Function.prototype.as_async = async function(...args) { return this(...args); };
Promise.prototype.map = async function(fmap) {
const f = async x => fmap(await x);
return [].concat(await this).map(f);
};
Promise.prototype.as_promise_all = async function() {
return Promise.all([].concat(await this));
};
Array.prototype.as_async = async function() { return this; };
이렇게 하면 필요한 await
와 await Promise.all([...])
를 제거할 수 있고 방법 체인을 통해서만 직렬과 병렬의 혼합 구조를 기술할 수 있다.측정할 때, 비동기 함수는 실행할 때 임의의 delay 값을 설정합니다.
// 実行する関数 ---
// ----------------
async function sleep(ms) {
const p = new Promise(resolve => setTimeout(resolve, ms));
await p;
}
async function asyncN(v) {
await sleep(v*100);
return v;
}
async function asyncx2(v) {
await sleep(v*200);
return v*2;
}
async function asyncx2inv(v) {
await sleep((10-v)*200);
return v*2;
}
function N(v) { return v; }
function x2(v) { return v*2; }
function sum(a) { return a.reduce((sum, elem) => sum + elem, 0); }
function mul(a) { return a.reduce((sum, elem) => sum * elem, 1); }
이외에 측정용 기준 함수도 준비했다.// ベンチ用 ---
// ------------
async function bench(f) {
const start = new Date();
console.log('result:', await f());
console.log('elapsed:', (new Date()).getTime() - start.getTime(), 'ms');
}
그러면 직렬 연결, 병렬, 직렬 연결과 병렬 혼합의 각 처리를 쓰십시오.직렬연결
병행과 비교하기 편리하도록 비동기 처리의 배열을 사용하여 측정하기 때문에 엄밀히 말하면 함수는 각 병렬 처리를 직렬로 연결하는 것이다.
// 100 ms, 200 ms, 300 ms
const src_ary = () => [asyncN(1), asyncN(2), asyncN(3)];
// 4300 ms
await bench(async () =>
src_ary().as_async().as_promise_all() // 100 ms, 200 ms, 300 ms
.map(x2).as_promise_all()
.map(asyncx2inv).as_promise_all() // 1600 ms, 1200 ms, 800 ms
.map(asyncx2).as_promise_all() // 800 ms, 1600 ms, 2400 ms
.map(x2).as_promise_all()
);
각 병행 처리 결과를 기다려야 하기 때문에 측정 시간은 각 병행 처리의 각 최대치를 더한 결과로 상기300 + 1600 + 2400
이기 때문에 4300ms의 시간이 필요하다.병렬하다
병행 평가는 한 차례
Promise.all()
만 진행된다.// 3500 ms
await bench(async () =>
src_ary().as_async() // 100 ms, 200 ms, 300 ms
.map(x2)
.map(asyncx2inv) // 1600 ms, 1200 ms, 800 ms
.map(asyncx2) // 800 ms, 1600 ms, 2400 ms
.map(x2)
.as_promise_all()
);
하나하나 처리를 수행할 수 있기 때문에 측정 시간은 각자의 합계 시간의 최대치이고 상기max(2500, 3000, 3500)
이기 때문에 3500ms의 시간이 필요하다.직렬 및 병렬 블렌드
마지막으로, 시작의 직렬과 병렬의 혼합 실행을 실현해 봅시다.
// 4600 ms
await bench(async () =>
// a1_1()
asyncN(2) // 200 ms
// a1_2()
.then(asyncx2) // 400 ms
.then(x => // 4
[
[
// a2A()
asyncN(x) // 400 ms
.then(y =>
[
// a2AA
asyncN(y), // 400 ms
// a2AB_1
asyncN(y) // 400 ms
// a2AB_2
.then(asyncx2), // 800 ms
]
.as_async()
.as_promise_all()
// a3_1
.then(sum) // [4, 8] -> 12
// a3_2
.then(asyncx2) // 2400 ms
),
// a2B_1
asyncN(x + 1) // 500 ms
// a2B_2
// -> [1, 2, 3, 4]
.then(y => {
let a = [];
for (let i = 1; i < y; i++) {
a.push(i);
}
return a;
})
// a2B_3
.map(y => y*y)
.as_promise_all()
.then(za => // [1, 4, 9, 16]
[
// a2BA_1
sum.as_async(za) // -> 30
/// a2BA_2
.then(asyncx2inv), // 0 ms
// a2BB
mul.as_async(za), // -> 576
]
.as_async()
.as_promise_all()
),
]
.as_async()
.as_promise_all()
// a5
// NOTE: 直列と並列で `[ a3_2(), [a2BA_2(), a2BB()] ]` となるので
// flattenして渡す
.then(a => sum(a.flat())), // [24, [60, 576]].flat() -> 660
// a2C
asyncN(x + 2) // 600 ms
.then(y =>
[
// a2CA_1
asyncN(y) // 600 ms
// a2CA_2
.then(asyncx2), // 1200 ms
// a2CB
asyncN(y), // 600 ms
]
.as_async()
.as_promise_all()
// a4
.then(mul) // [12, 6] -> 72
,
)
]
.as_async()
.as_promise_all(),
)
// a6
.then(sum) // [660, 72] -> 732
);
이쪽의 측정 시간은 다음과 같다.상술한 계산을 참고하다
200 + 400 +
max(
max(
400 + max(400, 400 + 800) + 0 + 2400,
500 + 0 + 0 + max(0 + 0, 0)
) + 0,
600 + max(600 + 1200, 600) + 0
) + 0
, 4600ms가 소요됩니다.또한 병목과 가장자리가 있음을 알 수 있다. 병목
a3_2()
에 해당하는 처리는 2400ms가 필요하기 때문에 이곳을 단축하면 전체적인 집행 시간이 짧아진다. 반대로 a2CA_2()
에 해당하는 처리는 1200밀리초가 필요하다.이곳은 아무리 짧아도 실행 시간은 변하지 않는다.또한
a2A()
부터 시작된 직렬 처리는 4000ms를 바꿀 수 없다. 예를 들어 a4()
에 대응하는 처리는 1600ms의 여분이 있다.전체 텍스트 인코딩
길어져서 외부 링크예요게재 코드가 포함된 전문과 실행 결과는 다음과 같다(Wandbox) 확인 가능합니다.
Reference
이 문제에 관하여(async/await의 병행·직렬 실행을 측정하면서 이해하기-JavaScript), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/wordi/articles/041f7172bc8d5f텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)