skip to Main Content

Here are the challenge directions:

Given an asynchronous function fn and a time t in milliseconds, return a new time limited version of the input function. fn takes arguments provided to the time limited function.

The time limited function should follow these rules:

  • If the fn completes within the time limit of t milliseconds, the time limited function should resolve with the result.
  • If the execution of the fn exceeds the time limit, the time limited function should reject with the string "Time Limit Exceeded".

I’ve tried these two bits of code:

// Function to return new time limited version of input function
const timeLimit = function(fn, t) {

    // Outer asynchronous function
    async function timeLimited(...args) {

        // Set start time to current time
        let startTime = Date.now()

        // Wait to get fulfillment value from function parameter
        const result = await fn(...args);

        // Duration will be current time minus the start time
        let duration = Date.now() - startTime;

        /* If duration is greater than the time parameter,
           throw time limit excession error */
        if (duration > t) throw new Error("Time Limit Exceeded");

        // Else return the result of the await function
        else return result;

    };

    // Return the asynchronous time-limited function
    return timeLimited; };

But I get this:

Output:

{"rejected":{},"time":100}

Expected:

{"rejected":"Time Limit Exceeded","time":50}
// Function·to·return·new·time·limited·version·of·input·function
const timeLimit = function(fn, t) {

    // Boolean to say error is false or true
    let hasError = false;

    // Outer asynchronous function
    async function timeLimited(...args) {

        // Set timer ID
        const timerId = setTimeout(() => hasError = true, t);

        // Constant to hold result
        const result = fn(...args);

        // Clear timeout
        clearTimeOut(timerId);

        // If there is an error, throw it
        if (hasError) throw new Error("Time Limit Exceeded");

        // Else return the result of the await function
        else return result;

    };

    // Return time limited function
    return timeLimited; };

But I get this:

Runtime Error

(node:21) Warning: Accessing non-existent property 'ListNode' of module exports inside circular dependency
(Use `nodejs run --trace-warnings ...` to show where the warning was created)
(node:21) Warning: Accessing non-existent property 'TreeNode' of module exports inside circular dependency
(node:21) Warning: Accessing non-existent property 'NestedInteger' of module exports inside circular dependency
node:internal/process/promises:289
            triggerUncaughtException(err, true /* fromPromise */);
            ^
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "Error".] {
  code: 'ERR_UNHANDLED_REJECTION'
}
Node.js v20.10.0

Here are the test cases:

const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
limited(150).catch(console.log); // "Time Limit Exceeded" at t=100ms

If anyone could help me fix my code, I would appreciate it.

2

Answers


  1. You will have to create your own custom error message like below:

    const timeLimit = function (fn, t) {
      async function timeLimited(...args) {
        let startTime = Date.now();
        const result = await fn(...args);
        let duration = Date.now() - startTime;
        if (duration > t) throw new CustomError("Time Limit Exceeded", duration);
        else return result;
      }
    
      return timeLimited;
    };
    
    function CustomError(message, duration) {
      return {
        rejected: new Error(message).message || "",
        duration: duration,
      };
    }
    
    Login or Signup to reply.
  2. Both attempts have some common and some different problems:

    The common problems:

    • You reject with an Error object, while the challenge says that the rejection reason should be a string. This is why you see "rejected":{} in the output object. So don’t do new Error.
    • Maybe not an issue, but if the testing framework tests that the this argument is passed correctly to fn, then this is an issue in your code: it does not pass the this argument. To achieve that you should work with fn.call or fn.apply.

    Then to the specific problems:

    The first attempt has this issue:

    • The promise given as argument to await has no knowledge of the time-out and so the async function will only resume when fn resolves, which might be well beyond the time-out period. And so the time at which the rejection is triggered is too late. The testing framework detects this time difference.

    The second attempt:

    • It has a typo in clearTimeOut, and so this will immediately trigger a runtime error, which apparently is leading to further unhandled errors in the asynchronous part of the testing code.
    • Once that bug is fixed, it will synchronously execute both clearTimeout and if (hasError). This means the setTimeout callback will never run, and even if it did, you already checked hasError before it could ever run, and it will always be false. Therefore this version of the code will never return a promise that gets rejected with "Time Limit Exceeded".

    This kind of time-out behaviour can be implemented with Promise.race: you provide it the fn promise and a promise that will reject after the time-out. Whichever resolves first will determine the resolution of the promise that Promise.race returns.

    Here is how that would look:

    // Promisified verion of setTimeout:
    const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
    
    // Function to return new time limited version of input function
    const timeLimit = (fn, t) =>
        function (...args) {
            return Promise.race([
                fn.call(this, ...args), 
                delay(t).then(() => { throw "Time Limit Exceeded"; })
            ]);
        };
    
    (async function main() {
        // Tests
        const fn1 = (n) => delay(1000).then(() => n*n);
    
        const timed1 = timeLimit(fn1, 1500);
        await timed1(3).then(console.log); // 9
    
        const timed2 = timeLimit(fn1, 500);
        await timed2(3).catch(console.log); // "Time Limit Exceeded"
    
        // Your test case
        const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100);
        limited(150).catch(console.log); // "Time Limit Exceeded"
    })();
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search