简体   繁体   English

在 Firebase Cloud Functions 中使用 async/await 时返回一个 Promise

[英]Returning a promise when using async/await in Firebase Cloud Functions

So I've happily been using async/await since node 8 is supported on Firebase Cloud Functions.因此,自从 Firebase Cloud Functions 支持节点 8 以来,我一直很高兴地使用 async/await。 I am struggling with 1 thing though.不过,我正在为一件事而苦苦挣扎。 When using callable functions, it is told that you have to return a promise in the function, otherwise it won't work correctly.使用可调用函数时,被告知必须在函数中返回一个promise,否则将无法正常工作。 When using raw promises, its clear to me how to use it:使用原始承诺时,我很清楚如何使用它:

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
    })
});

But now, with async await, I'm not sure how to return this "chain of promises":但是现在,有了异步等待,我不确定如何返回这个“承诺链”:

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?
});

Does somebody know?有人知道吗?

HTTP functions don't return a promise. HTTP 函数不返回承诺。 They just send a result.他们只是发送一个结果。 You still have to use promises correctly in order to send the result, but a return value is not required.您仍然必须正确使用 Promise 才能发送结果,但不需要返回值。 HTTP functions are terminated when the response is sent. HTTP 函数在响应发送时终止。 See the documentation for more details :有关 更多详细信息,请参阅文档

Terminate HTTP functions with res.redirect(), res.send(), or res.end().使用 res.redirect()、res.send() 或 res.end() 终止 HTTP 函数。

You nailed it with your example code.你用你的示例代码把它钉牢了。

Async/await is just a newer way of promise. Async/await 只是一种较新的承诺方式。 They can be used interchangeable.它们可以互换使用。

Here is an example promise and async/await of the same function.这是同一函数的示例 promise 和 async/await。

This这个

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

is equivalent to this:相当于:

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
  }
});

Note that in both cases, you are returning a promise at the end.请注意,在这两种情况下,您最后都会返回一个承诺。 The return value of this onCall function would be whatever nextPromise(result) is.这个 onCall 函数的返回值将是nextPromise(result)是什么。 Since you are returning nextPromsie(result) , you don't need to await it.由于您要返回nextPromsie(result) ,因此您无需等待它。

To see the code solution to your question look at the answer of dshukertjr .要查看您问题的代码解决方案,请查看dshukertjr的答案。

If you want to understand how to return a "chain of promises" with async/await, here is your answer:如果您想了解如何使用 async/await 返回“承诺链”,您的答案如下:

You cant !你不能! Why ?为什么 ? Because await is used to wait for a Promise to complete.因为 await 用于等待 Promise 完成。 Once await return a value their is no more Promise.一旦 await 返回一个值,它们就不再是 Promise。

So if you absolutely want to return a promise using await, you can wait for one of the two functions that return promises but not both.因此,如果您绝对想使用 await 返回一个 Promise,您可以等待返回 Promise 的两个函数之一,但不能同时等待。

Here is two way to do that:这是两种方法:

A : A :

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
    }
});

B:乙:

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

The only difference between A and B is that A waits for "PromiseMethod" to complete before returning a Promise. A 和 B 之间的唯一区别是 A 在返回 Promise 之前等待“PromiseMethod”完成。 Whereas B returns a Promise right after being called.而 B 在被调用后立即返回一个 Promise。

"await" is just syntax sugar for returning a Promise "await" 只是返回 Promise 的语法糖

When you write an async function, the code will actually exit the function and return a Promise at the first await it encounters.当你编写一个异步函数时,代码实际上会退出函数并在它遇到的第一个等待时返回一个 Promise。 All code after the await will be converted to a then(). await 之后的所有代码都将转换为 then()。

So for firebase writing code with async/await is perfectly save and in my experience even less error-prone, since I can more easily structure try&catch in my code!因此,对于 firebase 而言,使用 async/await 编写代码是完美的保存方式,而且根据我的经验,更不容易出错,因为我可以更轻松地在代码中构建 try&catch!

Proof:证明:

Just run this in your console:只需在控制台中运行它:

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

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

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

TL;DR: You don't need to change anything - if you write code with multiple awaits, this will be handled by firebase like a chain of promises and everything will just work. TL;DR:您不需要更改任何内容 - 如果您编写具有多个等待的代码,这将由 firebase 处理,就像一系列承诺一样,一切都会正常工作。

And since my answer was downvoted, here is an authorative code-sample by the google firebase team itself:由于我的回答被否决了,这里是 google firebase 团队本身的权威代码示例:

https://github.com/firebase/functions-samples/blob/master/quickstarts/uppercase/functions/index.js 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]
});

Seems, we have to wait for several Promises in way like this:看来,我们必须以这样的方式等待几个 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.

From async method whatever you return it gets wrapped in promise automatically.无论您返回什么异步方法,它都会自动包装在 Promise 中。 eg例如

const myFun = async () => {return 5}

 myFun();


// Output in the console
Promise {<fulfilled>: 5}

And you can chain with the returned result since it is a promise您可以链接返回的结果,因为它是一个承诺

Another example with enhancement as suggested in other answer另一个答案中建议的增强示例

 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

The return value of async-await function is Promise . async-await函数的返回值为Promise https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#return_value https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#return_value

So, what you did actually is returning a chain of promises.因此,您实际上所做的是返回一系列承诺。

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;
});

I have created test project on firebase and both function calls give same logs.我在 firebase 上创建了测试项目,两个函数调用都给出了相同的日志。 在此处输入图像描述

A solution in that case is Promise.all() .这种情况下的解决方案是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));
});

You may find more informations about promises in this article on freecodecamp.org/promise-all您可以在freecodecamp.org/promise-all上的这篇文章中找到有关 Promise 的更多信息

Since you need to return promise you can create the promise object and resolve/reject (return) your response from api after processing all the promises.由于您需要返回 Promise,您可以创建 Promise 对象并在处理完所有 Promise 后从 api 解析/拒绝(返回)您的响应。

Option 1:选项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)
        }
    })
});

Option 2:选项 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
        })
});

Solution解决方案

For an alternative method, you can use Promise.allSettled() .对于另一种方法,您可以使用Promise.allSettled() It is the best way to wait for all promises in the function to complete as well as provide an easy way to modify the final return.这是等待函数中所有承诺完成的最佳方式,同时也提供了一种修改最终返回的简单方法。

Excerpt from the documentation 文档摘录

The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise. Promise.allSettled()方法返回一个在所有给定的 Promise 都已实现或拒绝后解析的 Promise,并带有一组对象,每个对象都描述每个 Promise 的结果。

It is typically used when you have multiple asynchronous tasks that are not dependent on one another to complete successfully, or you'd always like to know the result of each promise.它通常用于当您有多个不依赖于彼此成功完成的异步任务,或者您总是想知道每个承诺的结果时。

Your updated code should be您更新的代码应该是

exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
    const res1 = await promiseMethod();
    const res2 = await nextPromise(res1);
    return Promise.allSettled([res1, res2]).then((results) => results.forEach((result) => console.log(result.status)));
});

Additional Info附加信息

You should also look into Promise object, it has some nice methods for such a situation.您还应该查看Promise对象,它有一些针对这种情况的好方法。 Read more at documentation link文档链接中阅读更多信息

Just convert to a Promise if required.如果需要,只需转换为 Promise。

Ie If nextPromise returns a Promise:即如果 nextPromise 返回一个 Promise:

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

On the other hand, if nextPromise is an async function, just convert it to a Promise:另一方面,如果 nextPromise 是一个异步函数,只需将其转换为 Promise:

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

you can also convert the result:您还可以转换结果:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM