如何从Promise返回的对象数组中随机抽取3个不重复对象?
问题分析与解决方案
先帮你梳理下代码里的几个核心问题,再给出修正后的方案:
- 混淆了Promise对象和实际数组:你定义的
locations是一个Promise实例,不是直接的数组。所以在.then()里直接调用locations.sort()是错误的,必须接收Promise resolve出来的数组参数来操作。 onSnapshot的使用场景不匹配:onSnapshot是用来实时监听集合数据变化的(每次数据更新都会触发回调),但Promise只能被resolve一次。如果你的需求只是单次获取数据,应该用get()方法来获取快照,而非onSnapshot。- 可选优化点:
sort(() => 0.5 - Math.random())这种洗牌方式并非完全公平,若需要更可靠的随机效果,推荐使用Fisher-Yates洗牌算法。
修正后的代码(单次获取数据场景)
// 单次获取符合条件的locations集合数据 const getLocations = admin.firestore() .collection('locations') .where('area', '==', game_area); getLocations.get() .then(snapshot => { // 将快照转换为带id的对象数组 const results = snapshot.docs.map(doc => { const data = doc.data(); data.id = doc.id; return data; }); // 随机抽取3个对象 const amountOfLocations = 3; // 用Fisher-Yates算法进行公平洗牌 const shuffled = [...results]; for (let i = shuffled.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } const selectedLocations = shuffled.slice(0, amountOfLocations); // 在这里处理选中的结果 console.log('选中的地点:', selectedLocations); }) .catch(err => { console.log(`ERROR IN QUERY: ${JSON.stringify(err)}`); });
如果需要实时监听数据变化
如果你的需求是实时监听集合更新,每次数据变化都重新随机抽取3个,那就不能用单个Promise,直接使用onSnapshot的回调即可:
const getLocations = admin.firestore() .collection('locations') .where('area', '==', game_area); // 实时监听数据变化,每次更新都会执行回调 getLocations.onSnapshot(snapshot => { const results = snapshot.docs.map(doc => { const data = doc.data(); data.id = doc.id; return data; }); // 随机抽取3个对象 const amountOfLocations = 3; const shuffled = [...results]; for (let i = shuffled.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } const selectedLocations = shuffled.slice(0, amountOfLocations); console.log('实时更新后的选中地点:', selectedLocations); }, err => { console.log(`ERROR IN LISTENER: ${JSON.stringify(err)}`); });
关键说明
get()vsonSnapshot:单次取数用get(),它返回的Promise只会在获取快照后resolve一次;实时监听数据变化才用onSnapshot。- 正确处理Promise结果:
.then()的回调函数必须接收resolve出来的实际数组,不能直接操作Promise对象。 - Fisher-Yates洗牌算法:能保证每个元素有均等概率出现在任意位置,比
sort(() => 0.5 - Math.random())的伪随机逻辑更可靠。
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

