Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async.queue with async/await style functions

I'm trying to create a function that builds a queue from an array of objects and then processes each object by calling a number of functions.

The processing functions are asynchronous functions which, prior to needing to queue, I'd implemented using the async/await pattern. I think this is necessary as each relies on the output of the previous and I don't want to have a tonne of nested promise.then's

i.e. previously I had:

await Promise.all(messages.map(async(message) => {
    let activity = await activityController.getActivity(message.activityId);
    let url = await SMSController.getUrl(message.Token);
    let smsSendResult = await SMSController.sendSMS(messageString, activity.mobileNo);
    // etc...
}

Now what I want to be able to do is:

let queue = async.queue((message, done) => {
     let activity = await activityController.getActivity(message.activityId);
     let smsSendResult = await SMSController.sendSMS(messageString, activity.mobileNo);
    // etc...
}

messages.forEach((message) => {
    queue.push(message);
})

I have the problem though that this results in

SyntaxError: await is only valid in async function

And I can't seem to quite get my head around how to get past this.

like image 945
Ross Coundon Avatar asked Nov 07 '22 10:11

Ross Coundon


2 Answers

You're looking for async.series, not async.queue:

series(tasks, callbackopt)

Run the functions in the tasks collection in series, each one running once the previous function has completed.

So just following the docs:

const messageCallbacks = messages.map(function(msg) {
    return async function(callback) {callback(await handleMessage(msg));
});


async.series(messageCallbacks,
// optional callback
function(err, results) {
    // results is now equal to whatever handleMessage resolves to
});

Without async:

async function asyncMessageQueue(messages) {
    const results = [];
    for(var i=0,l=messages.length; i<l; ++i) {
        results.push(await handleMessage(messages[i]));
    }
    return results;
}

async function handleMessage(message) {
        let activity = await activityController.getActivity(message.activityId);
        let url = await SMSController.getUrl(message.Token);
        let smsSendResult = await SMSController.sendSMS(messageString, activity.mobileNo);
        // rest of the code
};

This also allows you to provide the next message with any previous results:, just change await handleMessage(messages[i]) to await handleMessage(messages[i], results) and then:

async function handleMessage(message, prevResults) {
        // rest of the code
};
like image 105
Giulio Bambini Avatar answered Nov 15 '22 11:11

Giulio Bambini


I found the asyncify function in the async module which allows me to do this:

var queue = async.queue(async.asyncify(async (message, done) => {
    let url = await SMSController.getUrl(message.token);
    // etc...
}
like image 37
Ross Coundon Avatar answered Nov 15 '22 10:11

Ross Coundon