Correctly Link Multiple Functions in Javascript with await/async

Author: Dunk

Translator: Crazy Technology House


Reproduction is strictly prohibited without permission

Before I finish electrade In addition to their work, they helped a friend's team finish their project. Recently, we hope to build a Craiglist-style anonymous e-mail relay for this project, which includes "serverless" Google Firebase Function (the same as AWS Lambda, Azure Function, etc.). So far, I've found it easier to think about handling asynchronous operations with. then() callbacks, but I want to use async/await here because it reads more clearly. I find most articles about linking multiple functions useless because they tend to publish incomplete demo code that is copied and pasted from MSDN. There are some pitfalls in async/await that are difficult to debug, because I have encountered all these pitfalls, so I will publish my complete code here and explain my learning process.

This is the working code that connects multiple functions, waits to solve all the problems, and then the N sends the results. The main mistakes are:

  1. Each async function myFunction () {your code here >} declaration automatically wraps the entire asynchronous function code (that is, < your code here >) in a new Promise, then converts it to return x and adds resolve (x) to the code. But you also need to wait outside it (let y = await myFunction()) or it won't actually wait. This debugging is very annoying.
  2. In a cloud function, you must send a response with res.send(), otherwise the function will think it failed and run it again.

The following code does these things:

  • We have two normal synchronization functions getFieldsFromRequest() and extractCourseIdFromEmailAddress() -- no problem here.
  • Then we need the async function getEmailOfCourseWithCourseId() to get the e-mail address of the course from Firestore. We don't know how long it takes to get content from Firestore, so it's async, and we need to run the next two functions and return (or promise parse) courseEmail.
  • The next two functions, saveToCloudFirestore() and sendEmailInSendgrid(), cannot run and return courseEmail before getEmailOfCourseWithCourseId(), otherwise they will think courseEmail is undefined, which makes everything worse. Through the function getEmailOfCourseWithCourseId() above awaiting and passing courseEmail, these functions (and if operators) will wait until this happens (that is, it has been resolved) and run again.
  • Finally, before running saveToCloudFirestore () and sendEmailInSendgrid() and returning their values, we cannot send res.send(), otherwise our entire cloud function will be interrupted before the work is completed. To do this, we save the saveToCloudFireStore () and sendEmailInSendgrid() responses (what they return) into variables, the only purpose of which is to mark when the above functions are completed. This replaces. then(): It waits for the two variables (savedToCloud and sentEmail) to "arrive" (their Promise has been resolved) and then runs res.send().
  • For ease of reading, I've deleted try/catch packaging that you should do in practice. You should never catch errors, but deleting them will make the async/await concept easier to understand.
// this is the cloud function you can call over HTTP. It is basically for email relay:
// it gets an email from sendgrid, parses the fields, looks up the real email with the courseId,
// saves to FireStore and sends and email with sendgrid.
// Finally, it sends a res.send() to end the cloud function

// {* import a bunch of shit *}

// main function
exports.emailFunction = functions.https.onRequest(async (req, res) => {
  let fields = getFieldsFromRequest(req); // sync
  let courseId = extractCourseIdFromEmailAddress(fields); // sync
  let courseEmail = await getEmailOfCourseWithCourseId(courseId); // async
  let savedToCloud = await saveToCloudFirestore(fields, courseEmail, courseId); // async
  let sentEmail = await sendEmailWithSendgrid(fields, courseEmail);  // async
  res.status(200).send(savedToCloud, sentEmail); // Once sentEmail and saveToCloud have been returned (aka promises have been resolved, aka their functions have been run), res.send() will run so Firebase/SendGrid know that func worked. 

// Helper functions below
function getFieldsFromRequest(req) { // sync
    let fields = readTheFieldsFromReqWithBusboy(req)
    return fields;

function extractCourseIdFromEmailAddress(fields) { // sync
    let courseId =,'@'));
    return courseId;

async function getEmailOfCourseWithCourseId(courseId) { // async important
    let courseData = await database.get(courseId)
    let courseEmail =;
    return courseEmail; // due to function being labeled async above, this is the equivalent of wrapping the whole function in 'return new Promise(resolve) => {}' and then returning a 'resolve(result)'

async function sendEmailWithSendgrid(fields, courseEmail) { // async important
    let msg = {to: courseEmail, from: fields.from, text: fields.text}
    let sentEmail = await sendgrid.send(msg)
    return sentEmail; // due to function being labeled async above, this is the equivalent of wrapping the whole function in 'return new Promise(resolve) => {}' and then returning a 'resolve(result)'

async function saveToCloudFirestore(fields, courseEmail, courseId) { // async important
    let savedToCloud = await database.add(fields, courseEmail, courseId)
    return savedToCloud;

Finally, try {catch {} wraps the last three asynchronous functions and main functions to catch errors. In addition, database code cannot be copied intact -- it's only for illustrative purposes!

Wechat Public Number: Front-end Pioneer

Welcome to scan the two-dimensional code, pay attention to the public number, and push you fresh front-end technical articles every day.

Welcome to continue reading other highly praised articles in this column:

Keywords: Javascript Database Google AWS

Added by animuson on Thu, 22 Aug 2019 07:17:27 +0300