Mihir Kandoi
Mihir Kandoi

Reputation: 131

How do I avoid promise nesting in this case with Firebase Cloud Functions?

I have an Android app that sends a list of phone numbers to a Cloud Function in my project. The server is then supposed to retrieve a list of all phone numbers of the users, get an intersection (same elements) between the two arrays and then send back the Firebase Auth user ID of the users in the intersection array to the app.

This is what I came up with :-

// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();

const auth = admin.auth();
const _ = require('underscore');

exports.getUids = functions.https.onCall((data, context) => {
    return auth.listUsers().then(userRecords => {
        var allPhoneNumbers = [];
        userRecords.users.forEach(user => allPhoneNumbers.push(user.phoneNumber));
        var phoneNumbers = _.intersection(allPhoneNumbers, data);
        var Uids = [];
        //the next line is where I am nesting promises
        phoneNumbers.forEach(phoneNumber => auth.getUserByPhoneNumber(phoneNumber).then(user => Uids.push(user.uid)));
        return Uids;
    }).catch(error => console.error(error));
});

I have never programmed in JavaScript but I'm trying. It's been 2 days now and I just can't figure out how to get around this. I have tried atleast 10 different ways. I find promises really hard to deal with. Any help is appreciated.

Upvotes: 0

Views: 194

Answers (1)

Samuel Vaillant
Samuel Vaillant

Reputation: 3857

You can use Promise.all to avoid this issue. Note that the calls to getUserByPhoneNumber don't wait for completion. You have to chain promise to wait for it. Here is an implementation with regular Promise:

return auth
  .listUsers()
  .then(userRecords => {
    var allPhoneNumbers = [];
    userRecords.users.forEach(user => allPhoneNumbers.push(user.phoneNumber));
    var phoneNumbers = _.intersection(allPhoneNumbers, data);

    return Promise.all(
      phoneNumbers.map(phoneNumber => auth.getUserByPhoneNumber(phoneNumber))
    );
  })
  .then(users => {
    return users.map(user => user.uid);
  })
  .catch(error => {
    console.error(error);
  });

Here is an implementation with async/await:

return auth
  .listUsers()
  .then(async userRecords => {
    var allPhoneNumbers = [];
    userRecords.users.forEach(user => allPhoneNumbers.push(user.phoneNumber));
    var phoneNumbers = _.intersection(allPhoneNumbers, data);

    const users = await Promise.all(
      phoneNumbers.map(phoneNumber => auth.getUserByPhoneNumber(phoneNumber))
    );

    return users.map(user => user.uid);
  })
  .catch(error => {
    console.error(error);
  });

Upvotes: 2

Related Questions