如何衡量Promise的执行时间?

IT技术 javascript typescript es6-promise
2021-03-05 11:27:27

我正在尝试编写一个函数来测量另一个函数的执行时间:

export class Profiler {
    public measureSyncFunc(fn: () => any): Promise<number> {
        return new Promise<number>((resolve, reject) => {
            let elapsed = 0;

            let intervalId = window.setInterval(() => {
                elapsed += 1; // this is never called
            }, 1);

            this.execFunc(fn)
                .then((result: any) => {
                    window.clearInterval(intervalId);
                    resolve(elapsed);
                });
        });
    }

    private execFunc(fn: () => any): Promise<any> {
        return new Promise<any>((resolve, reject) => {
            resolve(fn());
        });
    }
}

然后我像这样使用它:

let array = generateRandomArray(100000);

instance.measureSyncFunc(bubbleSort(array))
    .then((elapsed: number) => {
        console.log(`end session: ${elapsed} seconds`);
        resolve();
    });

气泡排序功能是同步的,需要几秒钟才能完成。请参阅此处的代码

控制台中的结果是“结束会话:0 秒”,因为从不调用间隔回调。

你知道我怎么称呼它吗?非常感谢你们 !

5个回答

如果您要测量的函数始终是同步的,则实际上没有必要涉及 Promise。

由于您要测试的函数需要参数,因此最好将其包装在箭头函数中,以便能够使用另一个上下文调用它,而不必自己管理它的参数。

像这样简单的事情就可以了。

function measure(fn: () => void): number {
    let start = performance.now();
    fn();
    return performance.now() - start;
}

function longRunningFunction(n: number) {
    for (let i = 0; i < n; i++) {
        console.log(i);
    }
}

let duration = measure(() => {
    longRunningFunction(100);
});

console.log(`took ${duration} ms`);

如果你想测量一个异步函数(如果它返回一个Promise)解决的时间,你可以很容易地将代码更改为这样的:

function measurePromise(fn: () => Promise<any>): Promise<number> {
    let onPromiseDone = () => performance.now() - start;

    let start = performance.now();
    return fn().then(onPromiseDone, onPromiseDone);
}

function longPromise(delay: number) {
    return new Promise<string>((resolve) => {
        setTimeout(() => {
            resolve('Done');
        }, delay);
    });
}

measurePromise(() => longPromise(300))
    .then((duration) => {
        console.log(`promise took ${duration} ms`);
    });

注意:此解决方案使用 ES6 Promise,如果您正在使用其他东西,您可能需要对其进行调整,但逻辑应该是相同的。

您可以在此处查看 Playground 中的两个示例

正如@JaromandaX 在对该问题的评论中指出的那样,也许最重要的变化是不将长时间运行的函数结果传递给计时函数。
2021-04-28 11:27:27
无论如何(但归功于@JaromandaX)。无论 OP 的其余代码是好是坏,如果没有 lambda 构造(或等效结构),它就不可能工作。
2021-05-10 11:27:27
事实上,至少对于同步功能来说,这意味着整个执行已经完成。如果您不介意,我会将其添加为答案中的备注。:)
2021-05-14 11:27:27
普通函数也可以,但箭头函数在这种情况下更灵活..
2021-05-17 11:27:27

不要setInterval用来计算毫秒(它不准确滞后漂移,最小间隔约为 4 毫秒)。只需在执行前后获得两个时间戳。

function measureASyncFunc(fn: () => Promise<any>): Promise<number> {
    const start = Date.now();
    return fn.catch(() => {}).then(() => {
        const end = Date.now();
        const elapsed = end-start;
        return elapsed;
    });
}

为了获得更高的准确度,请替换Date.nowperformance.now

看看timeFnPromise和相关的测试用例

  • 目标函数在被包装的函数被调用时被包装和执行
  • 将完成/拒绝处理程序附加到底层 Promise,该 Promise 将目标函数返回值作为“ret”,并将经过的时间作为“elapsedTime”
  • 通过将参数传递给目标函数来支持参数

样品用途:

const wrappedFn = timeFnPromise(aFunctionThatReturnsAPromise)

wrappedFn()
.then((values)=>{
  const {ret, elapsedTime} = values
  console.log(`ret:[${ret}] elapsedTime:[${elapsedTime}]`)
})

也可以通过 NPM modulejschest 获得

这是我编写的一个简单的包装函数。它返回一个 Promise(通过 async 关键字),所以你可以用你的 Promise 调用它。我将时间值作为属性添加到响应中。如果您无法在响应中包含该值,则您需要在之后将其删除。

const stopwatchWrapper = async (promise) => {
  const startTime = Date.now()
  const resp = await promise
  resp.executionTime = Date.now() - startTime
  return resp
}

const axiosPromise = stopwatchWrapper(axios(reqSelected))
const response = await axiosPromise
console.log(response.executionTime)

最好澄清一下 toskv 提出的方法仅适用于解决单个Promise。如果我们想使用 Promise.all() 它返回的时间结果是错误的。

下面是 toskv 开发的代码示例,但使用 Promise.all()

使用 Promise.all() 进行测量

如果有人需要测量使用 Promise.all() 执行的每个Promise所需的时间,可以遵循的方法是利用拦截器并在那里进行时间测量