在从 firebase 收集数据之前呈现网页 - NodeJS 和 EJS
Posted
技术标签:
【中文标题】在从 firebase 收集数据之前呈现网页 - NodeJS 和 EJS【英文标题】:Webpage is rendering before data is collected from firebase - NodeJS and EJS 【发布时间】:2020-07-23 21:56:48 【问题描述】:我尝试过使用 async-await、.then 和 now promise。我是javascript开发的新手。
代码
indexRouter.get('/dashboard', checkSignIn, async(request, response) =>
snapshot = await db.doc('users/accounts').get()
sites = snapshot.data()['sites']
const userId = request.session.userId
snapshot = await db.doc(`users/$userId`).get()
var linkedSites = snapshot.data()['linked sites']
let getDs = new Promise((resolve,reject)=>
console.log("1")
linkedSites.forEach((site) =>
console.log("2")
db.doc(`users/$userId`).collection(site).get()
.then((snapshot)=>
console.log("3")
snapshot.forEach((doc) =>
console.log("4")
console.log(doc.id)
emailId = doc.id
keys = doc.data()['keys']
var passwordEncrypt = doc.data()['password']
password = cryptoJS.....
details.push(site:'email': emailId, 'password': password, 'keys': keys)
)
)
)
console.log("5")
console.log(details)
resolve(details)
);
getDs.then((details)=>
console.log("rendering")
response.render('dashboard', 'details':details, 'linkedSites': linkedSites, 'sites': sites)
)
我正在收到回复
1
2
2
5
[]
rendering
error: ...details not found in ejs
3
4
rsp121@gmail.com
3
4
test@gmail.com
根据输出,console.log(2) 之后的 db.doc 行似乎在一定时间后被执行,并且之前发送了 resolve(details)。
找到解决问题的方法:
indexRouter.get('/dashboard', checkSignIn, async(request, response) =>
snapshot = await db.doc('users/accounts').get()
sites = snapshot.data()['sites']
const userId = request.session.userId
snapshot = await db.doc(`users/$userId`).get()
var linkedSites = snapshot.data()['linked sites']
if(linkedSites)
const getSnapshot = (site) =>
return new Promise(resolve =>
db.doc(`users/$userId`).collection(site).get()
.then((snapshot) =>
snapshot.forEach((doc) =>
emailId = doc.id
keys = doc.data()['keys']
var passwordEncrypt = doc.data()['password']
password = cryptoJS
details[site] = 'email': emailId, 'password': password, 'keys': keys
resolve(true)
)
)
)
Promise.all(linkedSites.map(getSnapshot)).then(()=>
console.log(linkedSites)
response.set('Cache-Control', 'no-store, no-cache, must-revalidate, private')
response.render('dashboard', 'details':details, 'linkedSites': linkedSites, 'sites': sites)
)
【问题讨论】:
【参考方案1】:问题是你的承诺在db.doc
被解决之前解决了,因为你的 db.doc 承诺在一个循环中。所以,你应该使用promise.all
下面的代码应该适合你。
indexRouter.get("/dashboard", checkSignIn, async (request, response) =>
snapshot = await db.doc("users/accounts").get();
sites = snapshot.data()["sites"];
const userId = request.session.userId;
snapshot = await db.doc(`users/$userId`).get();
var linkedSites = snapshot.data()["linked sites"];
let getDs = new Promise((resolve, reject) =>
const promises = [];
console.log("1");
linkedSites.forEach((site) =>
console.log("2");
promises.push(
new Promise((internalResolve) =>
db.doc(`users/$userId`)
.collection(site)
.get()
.then((snapshot) =>
console.log("3");
snapshot.forEach((doc) =>
console.log("4");
console.log(doc.id);
emailId = doc.id;
keys = doc.data()["keys"];
var passwordEncrypt = doc.data()["password"];
password = cryptoJS;
details.push(
site:
email: emailId,
password: password,
keys: keys,
,
);
internalResolve();
);
);
)
);
);
Promise.all(promises).then(() =>
console.log("5");
console.log(details);
resolve(details);
);
);
getDs.then((details) =>
console.log("rendering");
return response.render("dashboard",
details: details,
linkedSites: linkedSites,
sites: sites,
);
);
);
async/await
更简洁。
indexRouter.get("/dashboard", checkSignIn, async (request, response) =>
snapshot = await db.doc("users/accounts").get();
sites = snapshot.data()["sites"];
const userId = request.session.userId;
snapshot = await db.doc(`users/$userId`).get();
var linkedSites = snapshot.data()["linked sites"];
console.log("1");
linkedSites.forEach(async (site) =>
console.log("2");
const snapshot = await db.doc(`users/$userId`).collection(site).get();
console.log("3");
snapshot.forEach((doc) =>
console.log("4");
console.log(doc.id);
emailId = doc.id;
keys = doc.data()["keys"];
var passwordEncrypt = doc.data()["password"];
password = cryptoJS;
details.push(
site:
email: emailId,
password: password,
keys: keys,
,
);
);
);
console.log("rendering");
return response.render("dashboard",
details: details,
linkedSites: linkedSites,
sites: sites,
);
);
【讨论】:
Async/Await 代码仍然导致同样的错误。承诺代码就像一个魅力。我不知道为什么 async-await 不起作用。它适用于代码的其他部分。 这很奇怪。【参考方案2】:这是您的代码已更正、优化并使用 ECMA 脚本的最后一个 especifications,错误是正如其他评论所说,您无需等待“新 Promise..”声明中的承诺结果。如果您没有***错误处理程序,请不要在异步函数中使用 try/catch。
优化首先是快照变量,这样您就可以并行获取数据,而不是安全地获取数据。
indexRouter.get('/dashboard', checkSignIn, async (request, response) =>
try
let details = [];
const userId = request.session.userId
let [snapshot1, snapshot2] = await Promise.all([db.doc('users/accounts').get(), await db.doc(`users/$userId`).get()])
let sites = snapshot1.data()['sites']
var linkedSites = snapshot2.data()['linked sites'];
await Promise.all(
linkedSites.map((site) =>
db.doc(`users/$userId`).collection(site).get()
.then((snapshot) =>
snapshot.forEach((doc) =>
emailId = doc.id
keys = doc.data()['keys']
var passwordEncrypt = doc.data()['password']
password = cryptoJS
details.push( site: 'email': emailId, 'password': password, 'keys': keys )
)
)
)
)
response.render('dashboard', 'details': details, 'linkedSites': linkedSites, 'sites': sites )
catch (e)
//Render error you want
)
linkedSites.map.... 返回一个 Promise 数组,这些 Promise 最终被包装在 Promise.all 中,Promise.all 等到所有 Promise 都完成,或者在最后一种情况下其中一个被拒绝,你的代码会去捕获没有到达 try 内的 response.render 行。您可以使用
避免在本地捕获地图中每个承诺的错误 .then((snapshot) =>
...
).catch(e=> /*Do something with the rror*/)
【讨论】:
以上是关于在从 firebase 收集数据之前呈现网页 - NodeJS 和 EJS的主要内容,如果未能解决你的问题,请参考以下文章