【问题标题】:Returning a promise when using async/await in Firebase Cloud Functions在 Firebase Cloud Functions 中使用 async/await 时返回一个 Promise
【发布时间】:2019-01-27 02:32:33
【问题描述】:

自从 Firebase Cloud Functions 支持节点 8 以来,我一直很高兴地使用 async/await。不过,我正在为一件事而苦苦挣扎。使用可调用函数时,被告知必须在函数中返回一个promise,否则将无法正常工作。使用原始承诺时,我很清楚如何使用它:

exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
    return promiseMethod().then((result) => {
        return nextPromise(result);
    }).then((result) => {
        return result;
    }).catch((err) => {
        // handle err
    })
});

但是现在,有了异步等待,我不确定如何返回这个“承诺链”:

exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
    const res1 = await promiseMethod();
    const res2 = await nextPromise(res1);
    return res2;
    // ??? Where to return the promise?
});

有人知道吗?

【问题讨论】:

  • “我不知道如何返回这个“承诺链”:”——正如你在你编写的代码中所做的那样。
  • 好的,但是 firebase 控制台上的日志告诉我,我调用了该函数 2 次,而实际上我只调用了 1 次。我认为这与以错误的方式返回这些承诺有关。
  • 您可以轻松设置示例项目并验证答案的声明。只需创建一个等待 10 秒然后返回结果的函数。如果你用 await 调用这个函数,firebase 函数会正确等待 10 秒然后返回结果。
  • 我已将 google-firebase 团队的权威代码示例添加到我的答案中。如果这还不足以支持它,我不知道。

标签: javascript firebase async-await google-cloud-functions


【解决方案1】:

HTTP 函数不返回承诺。他们只是发送一个结果。您仍然必须正确使用 Promise 才能发送结果,但不需要返回值。 HTTP 函数在响应发送时终止。见documentation for more details

使用 res.redirect()、res.send() 或 res.end() 终止 HTTP 函数。

【讨论】:

  • .onCall-functions 也是这样吗?当我调用我的函数一次时,firebase 控制面板上的记录器说我的函数被调用了两次。我认为这与兑现这些承诺有关。
  • 不,可调用函数要求您返回一个用您要发送给客户端的数据解析的承诺。这应该在文档中很清楚。
  • 没错,所以我最初的问题适用于可调用函数:使用 async/await 时如何返回此承诺
  • 好吧,您编辑了问题以显示一个可调用函数,而不是您最初拥有的 HTTP 函数。您实际上被问到了一个不同的问题,而不是第一个问题,这并不是 Stack Overflow 的工作方式。如果你完全改变问题的条款,你会迷惑所有人。
  • HTTPS 和 callable 在将数据发送回客户端的方式上非常不同。请花一些时间研究文档和示例以澄清这一点。
【解决方案2】:

如果需要,只需转换为 Promise。

即如果 nextPromise 返回一个 Promise:

exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
    const res1 = await promiseMethod();
    return nextPromise(res1);
});

另一方面,如果 nextPromise 是异步函数,只需将其转换为 Promise:

exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
    const res1 = await promiseMethod();
    return Promise.resolve(nextPromise(res1));
});

你也可以转换结果:

exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
    const res1 = await promiseMethod();
    const res2 = await nextPromise(res1);
    return Promise.resolve(res2);
});

【讨论】:

  • 这完全是不必要的:异步函数将在第一个“等待”时向调用者返回一个 Promise,最终将解析为您在 return 语句中返回的任何内容。将这个 Promise 包装在另一个 Promise 中没有任何效果。
  • 我举了 3 个例子。第一个是最佳的,其他两个不是,并且只是为了澄清有不止一种方法可以做同样的事情。
  • Promise.resolve 完全没有必要且具有误导性。您可以在这两种情况下将其删除,没有任何缺点。这只是不必要的代码,会分散异步函数的工作方式
【解决方案3】:

"await" 只是返回 Promise 的语法糖

当您编写异步函数时,代码实际上会退出该函数并在遇到第一个 await 时返回一个 Promise。 await 之后的所有代码都将转换为 then()。

因此,对于 firebase 而言,使用 async/await 编写代码非常省事,而且根据我的经验,更不容易出错,因为我可以更轻松地在我的代码中构建 try&catch!

证明:

只需在控制台中运行它:

async function iAmAsync() {
  await new Promise(r => window.setTimeout(r, 1000))
  return 'result'
}

let x = iAmAsync()
console.log(x)

将打印:Promise{<resolved>: "result"}

TL;DR:您无需更改任何内容 - 如果您编写具有多个等待的代码,这将由 firebase 处理,就像一系列承诺一样,一切都会正常工作。

由于我的回答被否决,这里是 google firebase 团队本身的权威代码示例:

https://github.com/firebase/functions-samples/blob/master/quickstarts/uppercase/functions/index.js

exports.addMessage = functions.https.onRequest(async (req, res) => {
// [END addMessageTrigger]
  // Grab the text parameter.
  const original = req.query.text;
  // [START adminSdkPush]
  // Push the new message into the Realtime Database using the Firebase Admin SDK.
  const snapshot = await admin.database().ref('/messages').push({original: original});
  // Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
  res.redirect(303, snapshot.ref.toString());
  // [END adminSdkPush]
});

【讨论】:

    【解决方案4】:

    看来,我们必须像这样等待几个 Promise:

    const listOfAsyncJobs = [];
    listOfAsyncJobs.push(createThumbnail(1, ...));
    listOfAsyncJobs.push(createThumbnail(2, ...));
    listOfAsyncJobs.push(createThumbnail(3, ...));
    ...
    return Promise.all(listOfAsyncJobs); // This will ensure we wait for the end of the three aync tasks above.
    

    【讨论】:

      【解决方案5】:

      无论你返回什么异步方法,它都会自动包装在 Promise 中。 例如

      const myFun = async () => {return 5}
      
       myFun();
      
      
      // Output in the console
      Promise {<fulfilled>: 5}
      

      你可以链接返回的结果,因为它是一个承诺

      另一个答案中建议的增强示例

       const myFun4 = async () => {
            const myNum = await new Promise(r => window.setTimeout(() => r(5), 1000));
            const myNum2 = await new Promise(r => window.setTimeout(() => r(5), 1000));
            return myNum + myNum2;
          }
          myFun4().then((n) => console.log(n));
          // Output
          10
      

      【讨论】:

        【解决方案6】:

        async-await函数的返回值为Promisehttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#return_value

        所以,您实际上所做的是返回一系列承诺。

        const nextPromise = () => {
            console.log('next promise!');
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('next promise result')
                }, 3000)
            });
        }
        
        const promiseMethod = () => {
            console.log('promise!');
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('promise result');
                }, 2000)
            });
        }
        
        exports.createBankAccount = functions.https.onCall((data, context) => {
            return promiseMethod().then((result) => {
                return nextPromise(result);
            }).then((result) => {
                return result;
            }).catch((err) => {
                // handle err
                console.log(err);
            })
        });
        
        
        exports.createBankAccountAsync = functions.https.onCall(async (data, context) => {
            const result = await promiseMethod();
            const res = await nextPromise(result);
            return res;
        });
        

        我在 firebase 上创建了测试项目,两个函数调用都给出了相同的日志。

        【讨论】:

          【解决方案7】:

          这种情况下的解决方案是Promise.all()

              exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
              const promises = [];
          
              const res1 = await promiseMethod();
              const res2 = await nextPromise(res1);
          
              promises.push(res1);
              promises.push(res2);
          
              // Here's the return of the promises
              return Promise.all(promises).catch(error => console.error(error));
          });
          

          你可以在freecodecamp.org/promise-all的这篇文章中找到更多关于 Promise 的信息

          【讨论】:

            【解决方案8】:

            您的示例代码成功了。

            Async/await 只是一种更新的承诺方式。它们可以互换使用。

            这是同一个函数的一个示例 promise 和 async/await。

            这个

            exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
                return promiseMethod().then((result) => {
                    return nextPromise(result);
                }).catch((err) => {
                    // handle error here
                })
            });
            

            等价于:

            exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
              try {
                const result = await promiseMethod();
                return nextPromise(result); // You are returning a promise here
              }catch(e) {
                // handle error here
              }
            });
            

            请注意,在这两种情况下,您最后都会返回一个承诺。此 onCall 函数的返回值将是 nextPromise(result) 的任何值。由于您返回的是nextPromsie(result),因此您无需等待。

            【讨论】:

              【解决方案9】:

              要查看问题的代码解决方案,请查看dshukertjr 的答案。

              如果您想了解如何使用 async/await 返回“承诺链”,以下是您的答案:

              你不能! 为什么 ?因为 await 用于等待 Promise 完成。一旦 await 返回一个值,它们就不再是 Promise。

              因此,如果您绝对想使用 await 返回一个 Promise,您可以等待返回 Promise 的两个函数之一,但不能同时等待。

              这里有两种方法:

              一个:

              exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
                  try {
                      const result = await promiseMethod();
                      return nextPromise(result); // You are returning a promise here
                  }catch(e) {
                      // handle error here
                  }
              });
              

              乙:

              exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
                  return promiseMethod().then(async (result) => {
                      return await nextPromise(result);
                  }).catch((err) => {
                      // handle err
                  })
              });
              

              A 和 B 之间的唯一区别是 A 在返回 Promise 之前等待“PromiseMethod”完成。而 B 在被调用后立即返回一个 Promise。

              【讨论】:

                【解决方案10】:

                由于您需要返回 Promise,因此您可以创建 Promise 对象并在处理完所有 Promise 后从 api 解析/拒绝(返回)您的响应。

                选项 1:

                exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
                    return new Promise(async (resolve, reject) => {
                        try {
                            const res1 = await promiseMethod();
                            const res2 = await nextPromise(res1);
                            // This will return response from api
                            resolve(res2);
                        }
                        catch (err) {
                            // Handle error here
                            // This will return error from api
                            reject(err)
                        }
                    })
                });
                

                选项 2:

                exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
                    return new Promise(async (resolve, reject) => {
                        const res1 = await promiseMethod();
                        const res2 = await nextPromise(res1);
                        // This will return response from api
                        resolve(res2);
                    })
                        .then((val) => val)
                        .catch((err) => {
                            // Handle error here
                            // This will return error from api
                            return err
                        })
                });
                

                【讨论】:

                • 遗憾的是,promise executor 函数不应该是异步的。
                猜你喜欢
                • 2020-11-29
                • 2022-08-22
                • 1970-01-01
                • 2017-09-11
                • 2021-12-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多