Promise 기간을 측정할 수 있습니까?
14890 단어 performancedenonodejavascript
TL;DR
첫 번째 시도
생성 및 해결 시간을 수동으로 지정하여 특정 지점에서 특정 약속의 기간을 측정할 수 있습니다.
const startInstant = performance.now();
fetch("https://httpbin.org/get").then(() => {
const endInstant = performance.now();
console.log(`Duration: ${endInstant-startInstant}ms`);
});
Duration: 447ms
await
콜백을 사용하는 대신 약속then
을 사용하는 경우에도 작동합니다.
const startInstant = performance.now();
await fetch("https://httpbin.org/get");
const endInstant = performance.now();
console.log(`Duration: ${endInstant-startInstant}ms`);
Duration: 288ms
그러나 약속의 기간을 측정하려는 일반 성능 라이브러리라면 어떻게 해야 할까요? 그리고 사용자가 애플리케이션 코드를 변경할 필요 없이 그렇게 하시겠습니까?
프로토타입을 조작하여 Promise API 자체를 시도할 수 있습니다monkeypatching.
// The generic performance library's code
const old_promise_constructor = Promise;
const old_promise_prototype = Promise.prototype;
Promise = function() {
const promiseObj = new old_promise_constructor(...arguments);
promiseObj.startInstant = performance.now();
return promiseObj;
}
Promise.prototype = old_promise_prototype;
const old_then = Promise.prototype.then;
Promise.prototype.then = function(onFulfilled) {
const startInstant = this.startInstant;
old_then.call(this, function(value) {
const endInstant = performance.now();
console.log(`Start instant: ${startInstant}`);
console.log(`End instant: ${endInstant}`);
console.log(`Duration: ${endInstant-startInstant}ms`);
onFulfilled(value);
});
}
// The (untouched) application code
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('something');
}, 300);
});
myPromise.then((value) => { console.log(`Promise resolved to ${value}`); });
Start instant: 223005
End instant: 223317
Duration: 312ms
Promise resolved to something
트릭을 수행하는 것 같습니다 ...?
더 블로커
그러나 이것은 코드 조각이 await
약속을 하는 경우 및/또는 약속이 "네이티브"(즉, 내장 함수에 의해 생성된 경우)인 경우에는 작동하지 않습니다.
const res = await fetch("https://httpbin.org/get");
await
-ing을 사용하면 약속이 해결될 때 연결할 수 없습니다. 그리고 기본 약속은 일반적으로 자신의 생성에 연결하는 방법을 제공하지 않습니다.
그래서 이것은 단지 불가능합니까?
구조를 위한 Node.js
유사한 문제를 해결하기 위한 전용 기능 덕분에 Node에서 가능합니다. 한 가지 예는 v16부터 릴리스된 PromiseHooks API입니다.
// The generic performance library's code
import { promiseHooks } from 'node:v8';
promiseHooks.onInit(function (promise) {
promise.startInstant = performance.now();
});
promiseHooks.onSettled(function (promise) {
console.log(`Duration ${performance.now() - promise.startInstant}ms`);
});
// The (untouched) application code
await fetch("https://httpbin.org/get"); //Needs Node v18+ to work without dependencies
Duration 40.9920469969511ms
Duration 0.13454999029636383ms
Duration 41.30363701283932ms
Duration 41.89799699187279ms
Duration 0.24492000043392181ms
Duration 41.59886699914932ms
Duration 228.2701609879732ms
Duration 201.04653500020504ms
Duration 229.50974099338055ms
Duration 1.0617499947547913ms
Duration 297.37966600060463ms
Duration 297.78996600210667ms
Duration 268.15292900800705ms
...
결과는 예제에서 await
-ed인 것 외에도 많은 내부 약속(Node/v8에서)을 선택하고 있음을 의미합니다. 이는 원하는 대로 모든 약속의 기간을 캡처하고 있음을 나타냅니다.
(AsyncLocalStorage API와 AsyncHooks API를 사용하여 동일한 목표를 달성하려고 했지만 방법을 알 수 없었습니다. Here’s what I tried )
그러나 브라우저 및 기타 JS 런타임(예: Deno, Deno Deploy, Cloudflare Workers, Bun)의 경우는 어떻습니까?
하지만 먼저, 애초에 이것이 왜 문제가 됩니까???
일부 동기 부여
애플리케이션 성능 모니터링(APM) 공급업체(예: Datadog, NewRelic 등)는 종종 소스 코드를 수정하지 않고도 앱의 동작을 기록할 수 있기를 원합니다("자동 계측"이라고 하는 방식). 사용 가능한 경우 라이브러리는 런타임이 노출하는 코드 실행에 대한 지정된 후크를 통해 이를 수행합니다(예: 노드의 AsyncHooks를 통해).
이들은 계측 코드를 동적으로 삽입할 수 있는 자연스러운 지점입니다. 그러나 이러한 확장 지점이 없으면 애플리케이션을 자동 계측하기가 어려울 수 있습니다.
일부 역사
행복한 부품
JS 생태계의 경우, 내가 찾을 수 있었던 이 문제에 대한 첫 번째 녹음된 토론은 this issue from 2015 on the Chromium bug tracker 이었습니다.
토론은 성능 모니터링을 더 쉽게 하기 위해 비동기 코드 주변의 v8 JS 엔진에 후크를 추가하는 방법에 관한 것입니다. 또한 다음을 포함하여 밀접하게 관련된 문제를 제기합니다.
const startInstant = performance.now();
fetch("https://httpbin.org/get").then(() => {
const endInstant = performance.now();
console.log(`Duration: ${endInstant-startInstant}ms`);
});
Duration: 447ms
const startInstant = performance.now();
await fetch("https://httpbin.org/get");
const endInstant = performance.now();
console.log(`Duration: ${endInstant-startInstant}ms`);
Duration: 288ms
// The generic performance library's code
const old_promise_constructor = Promise;
const old_promise_prototype = Promise.prototype;
Promise = function() {
const promiseObj = new old_promise_constructor(...arguments);
promiseObj.startInstant = performance.now();
return promiseObj;
}
Promise.prototype = old_promise_prototype;
const old_then = Promise.prototype.then;
Promise.prototype.then = function(onFulfilled) {
const startInstant = this.startInstant;
old_then.call(this, function(value) {
const endInstant = performance.now();
console.log(`Start instant: ${startInstant}`);
console.log(`End instant: ${endInstant}`);
console.log(`Duration: ${endInstant-startInstant}ms`);
onFulfilled(value);
});
}
// The (untouched) application code
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('something');
}, 300);
});
myPromise.then((value) => { console.log(`Promise resolved to ${value}`); });
Start instant: 223005
End instant: 223317
Duration: 312ms
Promise resolved to something
const res = await fetch("https://httpbin.org/get");
// The generic performance library's code
import { promiseHooks } from 'node:v8';
promiseHooks.onInit(function (promise) {
promise.startInstant = performance.now();
});
promiseHooks.onSettled(function (promise) {
console.log(`Duration ${performance.now() - promise.startInstant}ms`);
});
// The (untouched) application code
await fetch("https://httpbin.org/get"); //Needs Node v18+ to work without dependencies
Duration 40.9920469969511ms
Duration 0.13454999029636383ms
Duration 41.30363701283932ms
Duration 41.89799699187279ms
Duration 0.24492000043392181ms
Duration 41.59886699914932ms
Duration 228.2701609879732ms
Duration 201.04653500020504ms
Duration 229.50974099338055ms
Duration 1.0617499947547913ms
Duration 297.37966600060463ms
Duration 297.78996600210667ms
Duration 268.15292900800705ms
...
그 결과 Node가 v8(v8 런타임이 아닌 버전 8)의 AsyncHooks API, v14의 AsyncLocalStorage API, v16의 PromiseHooks API를 사용할 수 있게 해주는design doc 그리고 궁극적으로a change to v8가 나왔습니다.
오늘날 이들은 APM 도구가 Node.js 애플리케이션에 대한 자동 계측을 제공할 수 있도록 하는 핵심 API를 형성합니다.
슬픈 부분
불행히도 이러한 변경 사항에 대한 적응은 TC39 사양 제안을 본 적이 없습니다(this older one for zones도 a more recent one for async context도 아님). 이것은 모든 JS 런타임이 일관되게 구현하기 위한 표준 기반 API로 발전하지 않았다는 것을 의미합니다.
대신 맞춤형 API 표면이 있는 노드만 있고 다른 런타임은 동일한 APM 도구의 이점을 누릴 수 없습니다.
기대
Deno는 현재 Node가 원래 수행한 것과 동일한 기본 v8 변경 사항을 활용하여 동일한 목적creating its own API surface을 계획하고 있습니다.
그렇게 하면 OpenTelemetry(공급업체의 상용 APM 도구의 FOSS 버전으로 상상할 수 있음)가 Deno에서 제대로 작동할 수 있으며 실제로는 처음에 how I got started down this rabbit hole입니다. 😅
즉시 사용 가능하고 설정하기 쉬운 계측기는 모든 최신 생산 응용 프로그램에 반드시 있어야 합니다. 제 희망은 JS 런타임이 계속 발전함에 따라 이 측면에서도 모두 계속해서 개선되는 것입니다.
Reference
이 문제에 관하여(Promise 기간을 측정할 수 있습니까?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/grunet/can-you-measure-the-duration-of-a-promise-3a6h텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)