1

We're trying to write a Google Cloud Function that gets a translation from Google Translate API, and then write the results to our Firebase Firestore database. Each works alone, but together nothing works. In other words, we can get a translation from Google Translate. We can write data to Firestore. But if we try to do both, we don't get a translation back from Google Translate, and nothing is written to Firebase. We get no error messages. We've tried the code with async await and with promises. Here's the code with promises:

    exports.Google_EStranslateEN = functions.firestore.document('Users/{userID}/Spanish/Translation_Request').onUpdate((change, context) => {
      if (change.after.data().word != undefined) {
        const {Translate} = require('@google-cloud/translate');
        const projectId = 'myProject-cd99d';
        const translate = new Translate({
          projectId: projectId,
        });

        // The text to translate
        const text = change.after.data().word;
        // The target language
        const target = 'en';

        let translationArray = []; // clear translation array

        translate.translate(text, target)
        .then(results => {
          translation = results[0];
          translationArray.push(translation);

          try {
            // write translation to dictionary
admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(text).collection('Translations').doc('English').set({
              'translationArray': translationArray,
              'language': 'en',
              'longLanguage': 'English'
            })
            .then(function() {
              console.log("Translation written");
            })
            .catch(function(error) {
              console.error(error);
            });
          } catch (error) {
            console.error(error);
          }
        })
        .catch(error => {
          console.error('ERROR:', error);
        });
      }
    });

Here's the same code with async await:

exports.Google_EStranslateEN = functions.firestore.document('Users/{userID}/Spanish/Translation_Request').onUpdate((change, context) => { // triggers when browser writes a request word to the database
  if (change.after.data().word != undefined) {

async function getTranslation() {
  try {

    let translationArray = []; // clear translation array

    const {Translate} = require('@google-cloud/translate');
    const projectId = 'myProject-cd99d';

    const translate = new Translate({
      projectId: projectId,
    });

    // The text to translate
    const text = change.after.data().word;

    const options = {
      to: 'en',
      from: 'es',
      format: 'text'
    }

    let [translations] = await translate.translate(text, options);
    translations = Array.isArray(translations) ? translations : [translations]; // If translations is an array, leave it alone; if not, put it in an array
    translationArray.push(translations[0]);

    await admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(text).collection('Translations').doc('English').set({
      'translationArray': translationArray,
      'language': 'en',
      'longLanguage': 'English'
    })
    .then(function() {
      console.log("Translation written");
    })
    .catch(function(error) {
      console.error(error);
    });
    // };
  } catch (error) {
    console.error(error);
  }
} // close getTranslation
getTranslation();   
}
});
Thomas David Kehoe
  • 10,040
  • 14
  • 61
  • 100
  • I got the code working and posted it as [an answer to this question](https://stackoverflow.com/questions/54204396/how-to-return-a-promise-from-a-firebase-firestore-set-call/54210163#54210163). – Thomas David Kehoe Jan 16 '19 at 17:47

1 Answers1

2

You're not returning a promise that's resolved when all the async work is complete. If you don't do that, Cloud Functions assumes that all your work is complete, and will clamp down on all resources, and any pending work will be shut down.

The promise returned by translate.translate().then().catch() is being ignored. Your nested call to admin.firestore()...set() has a similar problem. It's not sufficient to just call then() and catch() on every promise because then() and catch() both return yet another promise.

You're also unnecessarily mixing usage of try/catch with catch() on the promise. You don't need both strategies for error handling, just one or the other.

When you used await in your second example, you forced JavaScript to pause until the async work represented by the promise returned by set() was complete. This allowed your function to return only after all the work was finished, which is why it worked correctly.

You might be helped by watching my video series on use of promises and async/await in Cloud Functions. Proper handling of promises is crucial to creating a correctly working function.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • Thanks, I've cleaned up my code with returns from translate.translate() and elsewhere in my code base for Firestore get() calls. I can't figure out how to return Firestore set() calls. return admin.Firestore()...set({...}) doesn't return anything, and kills the translate.translate() call. How do I return a Firestore set() call? – Thomas David Kehoe Jan 15 '19 at 16:52
  • I read your blog post "Why are the Firebase API asynchronous?" and tried var promise = admin.firestore()...set() promise.then() but this doesn't return anything and kill the translate.translate() call. – Thomas David Kehoe Jan 15 '19 at 17:15
  • If you have mulitple promises to wait on, use Promise.all(). – Doug Stevenson Jan 15 '19 at 17:18
  • Your video says that Promise.all() is for parallel async calls, not for sequential async calls. Am I misunderstanding this? – Thomas David Kehoe Jan 15 '19 at 17:56
  • Then I guess I don't know what you mean by "kill the translate call". – Doug Stevenson Jan 15 '19 at 18:15
  • I'm trying to run two functions sequentially. First, we call Google Translate and return a translation. Next, we take the translation and write it to Firestore. Each function works successfully alone, but together neither function works. If we call Firestore.set() then the Google Translate function doesn't execute or return anything. That's what I meant by Firestore.set() killing Google Translate. – Thomas David Kehoe Jan 15 '19 at 18:28
  • If you've rewritten your code, it sounds like you have a new problem now that's maybe different than the first. If you have a new problem, please ask a new question and show the new code that goes along with it. – Doug Stevenson Jan 15 '19 at 18:30
  • Thanks, I've asked a new question: https://stackoverflow.com/questions/54204396/how-to-return-a-promise-from-a-firebase-firestore-set-call – Thomas David Kehoe Jan 15 '19 at 18:42