SylphFeather
SylphFeather

Reputation: 435

Wrapper function that conditionally returning promise or callback

async function wait(callback){
  var awaited = await giveError();
  return returner(awaited, callback)
}


function giveError(){throw new Error('oops')}

I was tasked to create the returner function there, that would return a promise if there was no callback, and call the callback if it does exist

if returner does nothing but return its first argument, it would return a promise, as desired

async function returner(awaited, callback){
  console.log('triggering returner')
  if(!callback){
    return awaited
  } else {
    console.log('there is a callback')
  }
}

wait().then(console.log).catch(err=>{
  console.log('correct error catching here for async \n\n')
})

// prints 'correct error catching here for async'

the problem is that I have difficulty catching the error if there is a callback (with the assumption that awaited is a promise)

wait(function(err, val){
  console.log('callback error handling', err, val)
})

async function returner(awaited, callback){
  console.log('triggering returner')
  if(!callback){
    return awaited
  } else {
    awaited
    .then(val=>{
      callback(null, val)
    })
    .catch(err=>{
      callback(err)
    })
  }
}

// gives UnhandledPromiseRejectionWarning: Error: oops

I have questions:

why does 'triggering returner' never print?

is awaited a promise? If not, is it possible at all to write the returner function?

Upvotes: 0

Views: 171

Answers (2)

Jonas Wilms
Jonas Wilms

Reputation: 138317

why does 'triggering returner' never print?

Because await will behave like throw if the promise it is waiting for rejects. Therefore the code terminates there and the error bubbles up, the return returner(..) is never reached.

is awaited a promise?

No its the resolved value of the promise.


I would actually write a function that wraps the async function itself and takes the callback:

  const withCallback = fn => (...args) => {
    const last = args[args.length - 1];
    if(typeof last === "function") {
      return fn(...args.slice(0, -1))
         .then(res => last(null, res))
         .catch(last);
   } else {
      return fn(.. args);
   }
 };

That way you can write it as:

 const wait = withCallback(async function wait(){
   var awaited = await giveError();
   return "result";
 });

That allows you to do:

 wait().then(/*...*/).catch(/*...*/)
 // As well as
 wait(function(error, result) { /*...*/ });

Upvotes: 1

zero298
zero298

Reputation: 26878

async functions will always return a Promise. If you want to configure your function's return type to vary based on the arguments, you will need to do so manually:

function foo(cb) {
  if (typeof cb !== "function") {
    return Promise.resolve("hello");
  } else {
    return cb("hello");
  }
}

foo(v => console.log(v));
foo().then(v => console.log(v));

Upvotes: 0

Related Questions