Leo
Leo

Reputation: 443

Async await of a promise

I have to wait to func1 to be termined to run func2. But Since func1/2/3 contains promises it prints "termined" to early.

async function executeAsyncTask () {
              const res1 = await func1(a,b,c)
              const res2 = await func2(a,b,c)
              const res3 = await func2(a,b,c)

              return console.log(res1 , res2 , res3 )
            }

executeAsyncTask ()

func1

class A{

    promise_API_CALL(params){
      //some code here..
    }

    func1(a,b,c){

    //so work here...

    this.promise_API_CALL(params, function( data, err ) {
       if(err){console.error(err)}
      console.log( data );
      return data;
    });

    //so work here...
    console.log("termined")


}

EDIT: promise_API_CALL is a function of an external library

Upvotes: 4

Views: 241

Answers (4)

Carl Edwards
Carl Edwards

Reputation: 14464

Try wrapping the api call in a promise. Otherwise I can't see this working the way you want it to:

func1(a, b, c) {
  return new Promise((resolve, reject) => {
    this.promise_API_CALL(params, function(data, err) {
      if (err) {
        console.error(err)
        reject(err);
      }

      console.log(data);
      resolve(data);
    });

    //so work here...
    console.log("termined")
  });
}

Upvotes: 4

robe007
robe007

Reputation: 3947

In order to improve your code, the definition of executeAsyncTask should be like this:

async function executeAsyncTask () {
  try {

    const res1 = await func1(a,b,c)
    const res2 = await func2(a,b,c)
    const res3 = await func3(a,b,c)

    return [res1, res2, res3]; // Return all values from 'each await' as an array

  } catch (err) {
      throw 'Promise Rejected';
  }
}

As you can see, it uses try and catch even to handle the errors. In other words, if one of the await functions is rejected, then catch throws the error automatically.

// This 'func1 code' from 'Carl Edwards' is the same
func1(a, b, c) {
  return new Promise((resolve, reject) => {
    promise_API_CALL(params, function(data, err) {
      if (err) {
        console.error(err)
        reject(err);
      }

      console.log(data);
      resolve(data);
    });

    //so work here...
    console.log("termined")
  });
}

And finally you call executeAsyncTask like this:

executeAsyncTask().then(function(result) {
    console.log("result => " + result); // Result of 'res1, res2, res3'
}).catch(function(error) {
    console.log("error => " + error); // Throws 'Promise Rejected'
});

And remember:

  • Every async function returns a Promise object. The await statement operates on a Promise, waiting until the Promise resolves or rejects.

  • You can use await as many times as you like.


BONUS:

If you want all your promises (func1, func2, func3) execute in parallel (not one after another), you can modify your executeAsyncTask function like this:

async function executeAsyncTask () {
  try {

    return [ res1, res2, res3 ] = await Promise.all([
        func1(a,b,c),
        func2(a,b,c),
        func3(a,b,c)
    ])

  } catch (err) {
      throw 'Promise Rejected';
  }
}

Upvotes: 1

zero298
zero298

Reputation: 26920

This answer is very closely related to Carl Edward's answer but builds on node.js' conventions.

It's really unfortunate that promise_API_CALL()'s callback doesn't pass the error first. Otherwise you could have used util.promisify(). One alternative is to follow node.js' Custom promisified functions. It would look something like this:

const util = require("util");

promise_API_CALL[util.promisify.custom] = function (params) {
    return new Promise((resolve, reject) => {
        promise_API_CALL(params, function (data, err) {
            if (err) {
                return reject(err);
            }
            resolve(data);
        });
    });
};

The only issue that I see is that doing this mutates the original function (which isn't yours and is a little rude bad practice). But the issue is slightly mitigated since it uses ES6's new Symbol type which should mean that you won't clobber each other.

Here is a complete example:

const util = require("util");

/**
 * Use to force the API along the failure path
 * @constant {Boolean}
 */
const SHOULD_FAIL = false;

/**
 * Callback to deal with API responses
 * @callback apiCallback
 * @param {Object} data The data of the response
 * @param {Error} [err] Optional error that says something went wrong
 */

/**
 * Dummy API calling function
 * @param {Object} kwargs api arguments
 * @param {apiCallback} cb The callback that handles the response
 */
function apiCall(kwargs, cb) {
    setTimeout(() => {
        // Allow testing of failure path
        if (SHOULD_FAIL) {
            return cb(undefined, new Error("Purposefull failure"));
        }
        // Success path
        cb({
            foo: "bar"
        });
    }, 1000);
}

/*
 * Create a function that wraps the apiCall function in a Promise
 * and attach it to apiCall's util.promisify.custom Symbol
 */
apiCall[util.promisify.custom] = function (kwargs) {
    return new Promise((resolve, reject) => {
        apiCall(kwargs, (data, err) => {
            if (err) {
                return reject(err);
            }
            resolve(data);
        });
    });
};

// Create shorthand function to the promisified function
const asyncApiCall = util.promisify(apiCall);

// Sanity check to make sure that they are the same
console.log(`Are promisifies the same? ${asyncApiCall === apiCall[util.promisify.custom]}`);

// Run tester function
(async function main() {
    // Do some stuff
    console.log("Started");

    // Use the async func
    let some_data_from_api;
    try {
        some_data_from_api = await asyncApiCall({
            fizz: "buzz"
        });
    } catch (err) {
        console.error(err);
    }

    // Print the data after we have it
    console.log(some_data_from_api);

    //so work here...
    console.log("Done")
}());

Upvotes: 0

João Cunha
João Cunha

Reputation: 10307

In order for you code to work func1 would have to be like this:

async func1(a,b,c){
    const res = await promise_API_CALL(params, function( data, err ) {
       if(err){console.error(err)}
      console.log( data );
      return data;
    });

    console.log("termined");
    return res;
}

Then running this would work

async function executeAsyncTask () {
  const res1 = await func1(a,b,c);
  const res2 = await func2(a,b,c);
  const res3 = await func2(a,b,c);
  //yada yada yada
}

Upvotes: 0

Related Questions