专栏首页京程一灯用 await/async 正确链接 Javascript 中的多个函数[每日前端夜话0xAF]

用 await/async 正确链接 Javascript 中的多个函数[每日前端夜话0xAF]

在我完成 electrade【https://www.electrade.app/】 的工作之余,还帮助一个朋友的团队完成了他们的项目。最近,我们希望为这个项目构建一个 Craiglist 风格的匿名电子邮件中继,其中包含 “serverless” Google Firebase Function(与 AWS Lambda,Azure Function 等相同)。到目前为止,我发现用 .then() 回调处理异步操作更容易思考,但是我想在这里用 async/await,因为它读起来更清晰。我发现大多数关于链接多个函数的文章都没有用,因为他们倾向于发布从MSDN 复制粘贴的不完整的演示代码。在 async/await 上有一些难以调试的陷阱,因为我遇到了所有这些陷阱,所以我将在这里发布自己的完整代码并解释我的学习过程。

这是连接多个函数的工作代码,等待解决所有问题,然后 then 发送结果。主要错误是:

  1. 每个 async function myFunction(){ <your code here> } 声明自动将整个异步函数的代码(即 <your code here> )包装在 new Promise 中,然后转换为 return x 并在代码中加入 resolve(x)但是你还需要在它之外等待(即 let y = await myFunction()或它实际上不会等待。这个调试是非常烦人的。
  2. 在云函数中,你必须发送带有 res.send() 的响应,否则函数会认为它失败并重新运行它。

下面的代码要做这些事情:

  • 我们有 2 个正常的同步函数 getFieldsFromRequest()extractCourseIdFromEmailAddress() —— 这里没问题。
  • 然后我们需要 async 函数 getEmailOfCourseWithCourseId() 从Firestore获取课程的电子邮件地址。我们不知道从 Firestore 获取内容需要多长时间,因此它是 async 的,我们需要运行接下来的两个函数并返回(或以 promise 解析)courseEmail
  • 接下来的两个函数 saveToCloudFirestore()sendEmailInSendgrid(),不能在 getEmailOfCourseWithCourseId() 之前运行并返回 courseEmail,否则它们将认为 courseEmail 未定义,这样的话一切都变得糟透了。通过 awaiting 上面的函数 getEmailOfCourseWithCourseId() 并传递 courseEmail,这些函数(以及 if 运算符)将等到这种情况发生(也就是说已经解决),然后运再行。
  • 最后,在运行 saveToCloudFirestore()sendEmailInSendgrid() 并返回它们的值之前,不能发送 res.send(),否则我们的整个云函数将在工作完成之前中断。为此,我们将 saveToCloudFireStore()sendEmailInSendgrid() 响应(它们返回的内容)保存到变量中,其唯一目的是标记上述函数何时完成。这在某种意义上取代了 .then():它等待这两个变量( savedToCloudsentEmail)“到达”(他们的 Promise 已经解决),然后运行 res.send)()
  • 为了便于阅读,我已经删除了你应该在实践中进行的 try/catch 包装。你永远不应该捕获错误,但删除它们会使 async/await 概念更容易理解。
// 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 = fields.to.substring(0, fields.to.indexOf('@'));
    return courseId;
}

async function getEmailOfCourseWithCourseId(courseId) { // async important
    let courseData = await database.get(courseId)
    let courseEmail = courseData.email;
    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;
}

最后try {}catch {} 包装最后3个异步函数和主函数来捕获错误。此外,数据库代码不能原封不动的复制 —— 它仅用于说明目的! 原文:https://nikodunk.com/how-to-chain-functions-with-await-async/

本文分享自微信公众号 - 前端先锋(jingchengyideng),作者:疯狂的技术宅

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 实现一个简单的 JavaScript 状态机[每日前端夜话0xBF]

    使用状态机可以构建健壮的 UI,其好处已有详细的描述—— 例如你可以参见Edward J. Pring 的文章和 David Khourshid 的视频。另外K...

    疯狂的技术宅
  • 7 个令人惊讶的 JavaScript “特性”

    在过去的几个月里,我对 JSHint 做了一些改进,主要是,学习 ES6(我最自豪的是重新实现了变量作用域)的过程中我碰到了几个特性,它们让我惊讶,其中大部分是...

    疯狂的技术宅
  • 技术分享:用Node抓站(一)

    如果只写怎么抓取网页,肯定会被吐槽太水,满足不了读者的逼格要求,所以本文会通过不断的审视代码,做到令自己满意(撸码也要不断迸发新想法!

    疯狂的技术宅
  • 微信可发送定时消息,朋友圈打通明星点赞功能? | 晓技巧

    知晓君
  • 机器学习库/包的比较

    当涉及到训练计算机的行为而不需要明确的编程,存在大量的机器学习领域的工具。学术和工业界专业人士使用这些工具来构建从语音识别到MRI扫描中的癌症检测的许多应用。许...

    哒呵呵
  • ES6语法使用精华

    let 的作用域与 const 命令相同:只在声明所在的块级作用域内有效。且不存在变量提升 。

    前端_AWhile
  • 如何在大脑超载时代深入思考

    年初在弄房子装修的很多事情,可谓心力憔悴。那段时间上班时候就很难专注的思考,哪怕是持续短短的10分钟也显得力不从心。

    麦时
  • 如何在大脑超载时代深入思考

    年初在弄房子装修的很多事情,可谓心力憔悴。那段时间上班时候就很难专注的思考,哪怕是持续短短的10分钟也显得力不从心。

    已废弃
  • 那些必会用到的 ES6 精粹

    从接触 vue 到工作中用到 vue 将近 2 年了,在开发 vue 项目中用到了很多 es6 的 api ,es6 给我的开发带来了很大便利。

    夜尽天明
  • 从源码的角度浅谈Activity、Window、View之间的关系

    讲个很简单的例子,这一天天气甚好,小明外出写生,小明背了一包东西,画板啊,纸啊,笔啊什么的,然后小明找了一处风景甚好的地方,从包里拿出画板,纸,笔然后开始画画,...

    用户2802329

扫码关注云+社区

领取腾讯云代金券