簡體   English   中英

在 Firebase Cloud Functions 中使用 async/await 時返回一個 Promise

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

因此,自從 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?
});

有人知道嗎?

HTTP 函數不返回承諾。 他們只是發送一個結果。 您仍然必須正確使用 Promise 才能發送結果,但不需要返回值。 HTTP 函數在響應發送時終止。 有關 更多詳細信息,請參閱文檔

使用 res.redirect()、res.send() 或 res.end() 終止 HTTP 函數。

你用你的示例代碼把它釘牢了。

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) ,因此您無需等待它。

要查看您問題的代碼解決方案,請查看dshukertjr的答案。

如果您想了解如何使用 async/await 返回“承諾鏈”,您的答案如下:

你不能! 為什么 ? 因為 await 用於等待 Promise 完成。 一旦 await 返回一個值,它們就不再是 Promise。

因此,如果您絕對想使用 await 返回一個 Promise,您可以等待返回 Promise 的兩個函數之一,但不能同時等待。

這是兩種方法:

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

乙:

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。

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

當你編寫一個異步函數時,代碼實際上會退出函數並在它遇到的第一個等待時返回一個 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]
});

看來,我們必須以這樣的方式等待幾個 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.

無論您返回什么異步方法,它都會自動包裝在 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

async-await函數的返回值為Promise https://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 上創建了測試項目,兩個函數調用都給出了相同的日志。 在此處輸入圖像描述

這種情況下的解決方案是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 的更多信息

由於您需要返回 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.allSettled() 這是等待函數中所有承諾完成的最佳方式,同時也提供了一種修改最終返回的簡單方法。

文檔摘錄

Promise.allSettled()方法返回一個在所有給定的 Promise 都已實現或拒絕后解析的 Promise,並帶有一組對象,每個對象都描述每個 Promise 的結果。

它通常用於當您有多個不依賴於彼此成功完成的異步任務,或者您總是想知道每個承諾的結果時。

您更新的代碼應該是

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

附加信息

您還應該查看Promise對象,它有一些針對這種情況的好方法。 文檔鏈接中閱讀更多信息

如果需要,只需轉換為 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);
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM