3

I want to make a function decorator and measure async's function execution time, and return resolve or reject results as indented without decorating. Any clue how to achieve this? Most guides are about sync functions and are not touching the async world.

This is an example to illustrate the desired functionality

async exampleFunction () {
try{
  const response = await aPromise() // The goals is to measure this one with a decorator measure(aPromise) and dispatch actions if the fetching takes a long time
}
catch (err){
  // Do something with error
}
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
konsalex
  • 425
  • 5
  • 15

2 Answers2

2

Here's a higher-order function that can measure the time it takes for the returned promise to settle either by fulfilling or rejecting:

function measure(fn) {
  return async (...args) => {
    const begin = performance.now();
    try {
      return await fn(...args);
    } finally {
      const end = performance.now();
      console.log(`Execution time: ${end - begin}`);
    }
  };
}

Example usage:

exampleFunction();

async function exampleFunction() {
  try {
    const value = await measure(defer)('foo', 1000);
    // where normally you'd write:
    // const value = await defer('foo', 1000);
    console.log(`Resolved with ${value}`);
  } catch (reason) {
    console.log(`Rejected with ${reason}`);
  }
}

function measure(fn) {
  return async (...args) => {
    const begin = performance.now();
    try {
      return await fn(...args);
    } finally {
      const end = performance.now();
      console.log(`Execution time: ${end - begin}`);
    }
  };
}

function defer(result, ms) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const settle = Math.random() < 0.5 ? resolve : reject;
      settle(result);
    }, ms);
  });
}
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • Thanks for the detailed answer @patrick-roberts . Would be possible to avoid currying for example the part of `measure(defer)('foo', 1000)` to become `measure(defer('foo', 1000))` ? Is there any constraint, or just a preference? – konsalex Apr 15 '21 at 13:45
  • 1
    @konsalex sure, but I'd argue that's poorer design because at that point you're not measuring the entire lifetime of the promise (or at least not guaranteed to). That design would allow you to pass a promise that's been pending for some unknown amount of time. – Patrick Roberts Apr 15 '21 at 13:49
1

Something like this should do the trick:

export function MeasureExecutionTime(target, propertyKey, descriptor)  {
  const fun = descriptor.value;
  descriptor.value = async function(...args) {
    const start = performance.now();
    const result = await fun.apply(target, args);
    const end = performance.now();
    console.log(`Execution time: ${end - start}`);
    return result;
  }
}
Guerric P
  • 30,447
  • 6
  • 48
  • 86
  • 1
    Is this Typescript only, or works with vanilla js? – konsalex Apr 15 '21 at 13:08
  • 1
    @Guerric P The new function you assign in your code is never returning the original value. you need something like `const result = await fun(...args)` and at the end `return result` – David B. Apr 15 '21 at 13:09
  • It's a stage 2 proposal for JavaScript https://github.com/tc39/proposal-decorators so it's still experimental, you should use TypeScript at the moment to implement this. – Guerric P Apr 15 '21 at 13:09
  • Yes @DavidB. good catch – Guerric P Apr 15 '21 at 13:10
  • 1
    I could be way off-base here but I don't think OP is referring to the stage 2 decorator proposal, I think they just mean a higher-order function. – Patrick Roberts Apr 15 '21 at 13:11
  • well higher order functions obviously work but the `@MeasureExecutionTime` decorator syntax won't – Guerric P Apr 15 '21 at 13:12
  • 1
    Yeah but with this `MeasureExecutionTime` definition, it's not really clear as the answer stands how to actually use it without the decorator syntax. – Patrick Roberts Apr 15 '21 at 13:13
  • Yes, this would work with Class' method, but with enclosed function inside class methods' wouldn't work if I get this right – konsalex Apr 15 '21 at 13:14