![](/img/trans.png)
[英]Firebase Cloud Firestore query returning Promise { <pending> } instead of fulfilled when using async/await
[英]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。
當你編寫一個異步函數時,代碼實際上會退出函數並在它遇到的第一個等待時返回一個 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"}
由於我的回答被否決了,這里是 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;
});
這種情況下的解決方案是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.