Home > Blockchain >  escape loop using an error from async function?
escape loop using an error from async function?

Time:11-16

let me explain what I mean using an example

async function async_function(){
    await new Promise(r=>setTimeout(r,3000));
    throw 'task completed'
}

async function do_something_meanwhile() {
    await new Promise(r => setTimeout(r, 500));
    console.log(Math.floor(Math.random()*10));
}

(async ()=>{

    try {
        async_function(); //this returns an error after a while
        while (...)
            await do_something_meanwhile();
    } catch (err) { console.log('exited with error:',err) }

    console.log('moving on');
})();

I'm trying to run an async function and after it is complete immediately terminate the loop,
the best way I could think of (without any time delay) was to send an error

but it gives this error instead of moving on after it's done:

node:internal/process/promises:246
          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 "task 
completed".] {
  code: 'ERR_UNHANDLED_REJECTION'
}

is there a way around this or a better to achieve the desired effect?

CodePudding user response:

You can handle rejection by setting an error variable that you can check in the loop:

try {
    let error;
    async_function()
    .catch(err => error = err);
    while (...) {
        if (error) {
            throw error;
        }
        await do_something_meanwhile();
    }
} catch (err) {
    console.log('exited with error:',err)
}

If you need to proactively tell do_something_meanwhile to terminate as well, you could use an AbortController and pass its signal to do_something_meanwhile.

try {
    let error;
    const controller = new AbortController();
    const { signal } = controller;
    async_function()
    .catch(err => {
        error = err;
        controller.abort();
    });
    while (...) {
        if (error) {
            throw error;
        }
        await do_something_meanwhile(signal);
    }
} catch (err) {
    console.log('exited with error:',err)
}

I think if I were doing that, I might subclass AbortController so I can put the error in it:

class AbortContollerWithError extends AbortController {
    abort(error) {
        this.error = error;
        super.abort();
    }
}

then:

try {
    const controller = new AbortController();
    const { signal } = controller;
    async_function()
    .catch(err => {
        controller.abort(err);
    });
    while (...) {
        if (signal.aborted) {
            throw controller.error;
        }
        await do_something_meanwhile(signal);
    }
} catch (err) {
    console.log('exited with error:',err)
}

...or something along those lines.

You asked how you'd use the signal in do_something_meanwhile, and suggested in a comment that you're really using a timer in it. That's where the signal's abort event comes in handy, you can use that to settle the promise early:

async function do_something_meanwhile(signal) {
    let cancelError = {};
    try {
        await new Promise((resolve, reject) => {
            const timer = setTimeout(resolve, 500);
            signal.addEventListener("abort", () => {
                clearTimeout(timer);
                cancelError = new Error();
                reject(cancelError);
            });
        });
        console.log(Math.floor(Math.random() * 10));
    } catch (error) {
        if (error === cancelError) {
            // Probably do nothing
        } else {
            // Something else went wrong, re-throw
            throw error;
        }
    }
}

CodePudding user response:

Promise.all can run async_function and do_something_meanwhile in parallel mode.

While Promise/A doesn't have a cancel method, you can define a stopFlag, and check it in do_something_meanwhile function and the while loop.

let stopFlag = false

async function async_function() {
  await new Promise(r=>setTimeout(r, 3000));
  throw 'task completed'
}

async function do_something_meanwhile() {
  await new Promise(r => setTimeout(r, 500));
  if (!stopFlag) {
    console.log(Math.floor(Math.random() * 10));
  }
}

(async()=>{
  try {
    await Promise.all([
      async_function().catch((err) => {
        stopFlag = true
        throw err
      }), // this returns an error after a while
      (async () => {
        while (!stopFlag)
          await do_something_meanwhile();
      })()
    ])
  } catch (err) {
    console.log('exited with error:', err) 
  }

  console.log('moving on');
})();
  • Related