Rasmus Puls
Rasmus Puls

Reputation: 3219

Do something after multiple async mongoose calls finishes

I have a function that looks up data in several different documents, add some of the date from each document to a object and returns the object with the combines data from the different documents. The only problem is that the object is returned before any of the transactions are completed. I have googled a lot, and the only solution i can find is on Stack Overflow, but from 2012.... There must be some "newer" better way to do it? Preferably without installing more npm stuff.

Here is my function

function getPreviousEventCard(event, callback) {
    const card = {};
    card.dateStart = event.dateStart;
    card.dateEnd = event.dateEnd;
    card.userID = event.userID;
    card.adminID = event.adminID;

    // 1st async call
    Profile.getFullName(event.userID, (err, name) => {
        if (err) return callback(err, null);
        card.bsName = name.fullName;
    });

    // 2nd async call
    Profile.getProfileByUserId(event.parentID, (err, profile) => {
        if (err) return callback(err, null);
        card.parentName = profile.fullName;
        card.address = `${profile.address} ${profile.zip}`
    });

    // somehow wait until both(all) async calls are done and then:
    return callback(null, card);
}

btw, 'Profile' is a mongoose schema, and the get methods are using the findOne() method.

I have tried to nest the functions and have the return callback as the most inner, but then it is never returned for some reason.

Upvotes: 1

Views: 230

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074335

If you're using NodeJS v8 or higher, I would promisify those functions:

const getFullName = promisify(Profile.getFullName);
const getProfileByUserId = promisify(Profile.getProfileByUserId);

...and then use Promise.all:

function getPreviousEventCard(event, callback) {
    Promise.all([
        // 1st async call
        getFullName(event.userID),
        // 2nd async call
        getProfileByUserId(event.parentID)
     ])
    .then(([name, profile]) => {
        const card = {
            dateStart: event.dateStart,
            dateEnd: event.dateEnd,
            userID: event.userID,
            adminID: event.adminID,
            bsName: name.fullName,
            parentName: profile.fullName,
            address: `${profile.address} ${profile.zip}`;
        };
        callback(null, card);
    })
    .catch(err => callback(err);
}

or better yet, make getPreviousEventCard return a promise:

function getPreviousEventCard(event) {
    return Promise.all([
        // 1st async call
        getFullName(event.userID),
        // 2nd async call
        getProfileByUserId(event.parentID)
     ])
    .then(([name, profile]) => {
        return {
            dateStart: event.dateStart,
            dateEnd: event.dateEnd,
            userID: event.userID,
            adminID: event.adminID,
            bsName: name.fullName,
            parentName: profile.fullName,
            address: `${profile.address} ${profile.zip}`;
        };
    });
}

Preferably without installing more npm stuff

If you did want to install more npm stuff, there's an npm module that will promisify an entire API (rather than function by function) using a declarative description of the APIs functions: https://www.npmjs.com/package/promisify

Upvotes: 1

Related Questions