Firebase 简单查询给出错误:"Converting circular structure to JSON at JSON.stringify"



我正试图循环通过/users路径,并将其加入我的auth((。listUsers的结果是:

https://github.com/QuantumInformation/svelte-fullstack-starter/blob/master/firebase_specific/functions/src/users.ts#L32

export async function getSomeUsers(amount: number) {
try {
const listUsersResult = await admin.auth().listUsers(amount)
const parsedUsers = listUsersResult.users.map(stripUserSensitiveInfo).map(async user => {
console.log("try read_______________" + user.uid)
let userProfileSnapshot = await admin
.database()
.ref("users/" + user.uid)
.once("value")
console.log("end try read_______________" + user.uid)
return { ...user, userProfileSnapshot }
})
return parsedUsers
} catch (error) {
console.error("Error listing users:", error)
throw new Error("Error users" + error)
}
}

给出这个错误

在JSON.stringify((的stringify 上将循环结构转换为JSON

但此代码运行良好

export async function getSomeUsers(amount: number) {
try {
const listUsersResult = await admin.auth().listUsers(amount)

const parsedUsers = listUsersResult.users.map(stripUserSensitiveInfo).map( user => {

return 1
})

return parsedUsers
} catch (error) {
console.error("Error listing users:", error)
throw new Error("Error users" + error)
}
}

问题出现在map运算符中嵌入的异步调用中为了使它发挥作用,您需要等待每次迭代,这也是非常低效的。这是因为对于每一个额外的查询,所花费的时间都会线性增加。我的建议是以可扩展的方式转换整个逻辑。幸运的是,listAllUsers()函数在一次调用时返回所有结果

export async function getSomeUsers(amount: number) {
try {
/**
* fetch all the users with the provided limit
*/
const allUsers = await admin.auth().listUsers(amount)
/**
* loop through the returned values and create a promise to fetch
* Each of their document
*/
const listUsersResult = allUsers.users.map(user => {
return admin
.database()
.ref("users/" + user.uid)
.once("value")
})
/**
* When all the user documents have been fetched, iterare through them and deduce their values
*/
const parsedUsers = await Promise.all(listUsersResult).then(docSnashots => {
return docSnashots.map(snapshot => {
console.log("end try read_______________" + snapshot.val())
/**
* The records need to be matched with the original values
*/
const originalUserData = allUsers.users.find(u => u.uid === snapshot.key)
return { ...originalUserData, ...snapshot.val() }
})
})
return parsedUsers
} catch (error) {
console.error("Error listing users:", error)
throw new Error("Error users" + error)
}
}

注意由于循环,此方法的计算量更大,但由于每个读取操作都独立于其他操作,因此更节省时间。它可以针对这样的情况进行修改,即你确信读取操作总是需要很短的时间,并且(也许(比循环的计算更具资源效率

根据您的第二个代码片段,我认为异步调用中存在问题。你试过映射中的异步是否按预期工作了吗?。试着这样检查一下。

let parsedUsers = [];
const userList = listUsersResult.users.map(stripUserSensitiveInfo);
for (const user of userList) {
try {
let userProfileSnapshot = await admin
.database()
.ref("users/" + user.uid)
.once("value")
parsedUsers.push({...user, userProfileSnapshot});
} catch (error) {
console.error('Error on API', error);
}
}

您不能这样使用它。对于第二个map函数,它将返回一组promise。因此,您必须遵守所有的承诺,然后使用Promise.all来解决这些问题。

return Promise.all(parsedUsers);

查看了您链接的github repo。。。我真的不明白你是如何达到的地步的

在JSON.stringify((的stringify 上将循环结构转换为JSON

因为代码

export const listUsers = functions.https.onRequest(async (req, res) => {
...
const data = await getSomeUsers(10)
res.json(data)
})

在firebase_specific/functions/src/index.ts中应该看起来像

export const listUsers = functions.https.onRequest(async (req, res) => {
...
const data = await Promise.all(await getSomeUsers(10))
res.json(data)
})

或者,更好的是,正如@AlbertPinto所指出的,return parsedUsers应该改为return Promise.all(parsedUsers)

无论如何,返回错误消息:

userProfileSnapshotDataSnapshot对象,根据错误判断,具有循环引用。所以你可能应该改变

return { ...user, userProfileSnapshot }

return { ...user, userProfileSnapshot.val() }

或类似的东西,基于您的需求

在stringify 的JSON.stringify((上将循环结构转换为JSON

此错误是由字符串化圆形结构引起的。圆形结构是指具有引用自身的属性/深层属性的结构。

const a = {}
a.b = a; // this creates a circular structure

首先您应该检查stripUserSensitiveInfo函数是否创建了一个循环结构。这可能是因为当user对象是函数的结果并在第一个代码段中返回时发生了错误。

第二次如本评论所述https://stackoverflow.com/a/60299145/5381781,在第一个代码段中,变量parsedUsers实际上包含promise数组,而不是用户数据数组。要解决一系列承诺,请使用Promise.all()

const parsedUsers = await Promise.all(listUsersResult.users
.map(stripUserSensitiveInfo)
.map(async user => {
console.log("try read_______________" + user.uid);
let userProfileSnapshot = await admin
.database()
.ref("users/" + user.uid)
.once("value");
console.log("end try read_______________" + user.uid);
return { ...user, userProfileSnapshot };  // You might also want to tweak this structure
}));

另一个,有点脱离主题,假设admin.database().ref("users/" + user.uid).once("value")是一个网络调用,您可能希望使用for...of statement而不是映射。这是为了确保您的应用程序不会因发出过多的网络请求而淹没网络连接,具体取决于amount变量的值。

最新更新