You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Promise返回的对象数组中随机抽取3个不重复对象?

问题分析与解决方案

先帮你梳理下代码里的几个核心问题,再给出修正后的方案:

  1. 混淆了Promise对象和实际数组:你定义的locations是一个Promise实例,不是直接的数组。所以在.then()里直接调用locations.sort()是错误的,必须接收Promise resolve出来的数组参数来操作。
  2. onSnapshot的使用场景不匹配:onSnapshot是用来实时监听集合数据变化的(每次数据更新都会触发回调),但Promise只能被resolve一次。如果你的需求只是单次获取数据,应该用get()方法来获取快照,而非onSnapshot。
  3. 可选优化点: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() vs onSnapshot:单次取数用get(),它返回的Promise只会在获取快照后resolve一次;实时监听数据变化才用onSnapshot。
  • 正确处理Promise结果:.then()的回调函数必须接收resolve出来的实际数组,不能直接操作Promise对象。
  • Fisher-Yates洗牌算法:能保证每个元素有均等概率出现在任意位置,比sort(() => 0.5 - Math.random())的伪随机逻辑更可靠。

内容的提问来源于stack exchange,提问作者Jason

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 15:47:53