简体   繁体   English

当大量文档从 Firestore 快照到达时,如何不让 ReactJS 应用程序冻结?

[英]How not to make a ReactJS app freeze when a huge number of docs arrive from a Firestore snapshot?

I've a ReactJS application made up of 3 simple elements: 2 numbers and an input text.我有一个由 3 个简单元素组成的 ReactJS 应用程序:2 个数字和一个输入文本。 This is a test page of course.这当然是一个测试页面。
The 2 numbers refers to the number of elements that are present in 2 separate Firestore collections, and the input text doesn't do anything. 2 个数字是指存在于 2 个单独的 Firestore collections 中的元素数量,输入文本不执行任何操作。
The goal of my experiment is to understand why if I write a huge amount of docs in a short time in the collections (I'm using k6 in order to) and just print those two numbers by listening to Firestore snapshots, my page completely freezes until all the messages have arrived.我实验的目标是了解为什么如果我在短时间内在 collections 中编写大量文档(我使用k6以便)并通过收听 Firestore 快照打印这两个数字,我的页面会完全冻结直到所有消息都到达。

Here is my code这是我的代码

const App = () => {
  const [col1, setCol1] = useState(0);
  const [col2, setCol2] = useState(0);
  const [value, setValue] = useState('');
  useEffect(() => {
    console.debug('*** EFFECT');

    db
      .collection('abcd/efgh/collection1')
      .orderBy('timestamp')
      .startAt(new Date())
      .limitToLast(1)
      .onSnapshot(({ docs }) => {
        setCol1((e) => e + 1);
      });

    db
      .collection('qwer/tyui/collection2')
      .orderBy('timestamp')
      .onSnapshot(({ docs }) => {
        console.debug('***', docs.length);
        setCol2(docs.length);
      });
  }, []);

  return (
    <>
      {exist}

      <br />

      {messages}

      <br />

      <Input onChange={({ target: { value } }) => setValue(value)} value={value} />
    </>
  );
};

Of course the final target is to understand how to not cause the freeze.当然,最终目标是了解如何不导致冻结。
From this I see that spawning a separate thread will help, so the equivalent in JS desktop are WebWorkers.这里我看到产生一个单独的线程会有所帮助,所以 JS 桌面中的等价物是 WebWorkers。
Could that be a right idea?这可能是一个正确的想法吗? Do you have any other suggestion?你还有什么建议吗?

As per my understanding, I don't think snapshot is the right use here if you are just trying to calculate the document counts.根据我的理解,如果您只是想计算文档数量,我认为快照在这里不合适。 The onSnapshot() method is mainly for real time updates use cases. onSnapshot() 方法主要用于实时更新用例。 Each time the contents change/add/delete, another call updates the snapshot.每次内容更改/添加/删除时,另一个调用会更新快照。 So with a huge amount of documents, the snapshots are frequently updating until everything is caught up.因此,对于大量文档,快照会经常更新,直到一切都赶上。 You can review this page for some general onSnapshot use cases.您可以查看此页面以了解一些常规 onSnapshot 用例。 you can limit the write rate, and exceeding the limit can cause contention and latency issues .您可以限制写入速率,超过限制可能会导致争用和延迟问题

If you are just trying to do a bulk write update, and after that you would like to know how many documents are in the collection, then you can do the updates first, and use get() to retrieve the count of documents instead of onSnapshot to constantly get the update.如果您只是尝试进行批量写入更新,之后您想知道集合中有多少文档,那么您可以先进行更新,然后使用 get() 来检索文档数而不是 onSnapshot不断获得更新。 There are many ways to retrieve the count of documents, for example get() method or using a counter to keep track of the count.有很多方法可以检索文档计数,例如 get() 方法或使用计数器来跟踪计数。 third-party post .第三方帖子

I had basically the exact same issue, so I spent a bunch of time testing this myself, and here's what I found.我有基本上完全相同的问题,所以我花了很多时间自己测试,这就是我发现的。

Debouncing State Updates: Helps a little .去抖 State 更新:有点帮助

Basically, if you're constantly re-rendering large amounts of data, you can use a lot of memory and/or drop frames, so I assumed that debouncing the function would help.基本上,如果你不断地重新渲染大量数据,你可以使用很多 memory 和/或丢帧,所以我认为去抖动 function 会有所帮助。

It did help a bit but the app would still freeze or crash if more than a few hundred rows were updated.它确实有所帮助,但如果更新了数百行,应用程序仍会冻结或崩溃。

Here's how I implemented:以下是我的实现方式:

const debounce = (func, wait) => {
    let timeout;

    return function executedFunction(...args) {
        const later = () => {
            clearTimeout(timeout);
            func(...args);
        };

        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
};

... ...

    const debouncedSetItems = useMemo(() =>
        debounce((newItems) => {
            setCol1(newItems);
        }, 5000), []
    );

... ...

    useEffect(() => {
        const unsubscribe = firestore.collection("myCollection").onSnapshot((querySnapshot) => {
            const newItems = [];
            querySnapshot.forEach((doc) => {
                newItems.push({
                    id: doc.id,
                    ...doc.data()
                });
            });
    
            debouncedSetItems(newItems);

        });
 }, [debouncedSetItems]);

Slowing the Write Speed : Helps a lot;降低写入速度有很大帮助; has limitations.有局限性。

Essentially, if you can pause every once in a while when writing large amounts of data, the app won't hang.本质上,如果您可以在写入大量数据时每隔一段时间暂停一次,应用程序就不会挂起。

I was doing large numbers of writes via a cloud function, so what I did was pause for a few seconds every 50 writes:我正在通过云 function 进行大量写入,所以我所做的是每 50 次写入暂停几秒钟:

async function sleep(milliseconds) {
    return new Promise((resolve) => setTimeout(resolve, milliseconds));
}

... ...

if (i > 0 && i % 50 === 0) {
    console.log("Sleeping for 5 seconds");
    await sleep(5000);
}

This totally fixed the issue, but it has the obvious limitation of slowing your writes down, which may or may not be an issue.这完全解决了这个问题,但它有明显的限制,即减慢你的写入速度,这可能是也可能不是问题。 For me, it wasn't a huge deal because I still had plenty of buffer leftover in the 9-minute cloud function runtime limit, but your mileage may vary.对我来说,这并不是什么大问题,因为我在 9 分钟云 function 运行时限制中仍有大量剩余缓冲区,但您的里程可能会有所不同。

Curious to see how others have solved this.很想知道其他人是如何解决这个问题的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从 Firestore 数据库获取 collections 和文档? - How to get collections and docs from Firestore Database? 获取文档上 Firestore 快照监听器的数量 - Get the number of Firestore snapshot listeners on a document Flutter/Firestore - 查询快照不包括所有文档 - Flutter/Firestore - query snapshot does not include all docs Firestore 在 Where 条件发生变化时生成快照 - Firestore to Produce a Snapshot When the Where Condition Changes 从 Firestore 侦听器返回零快照文档 - Returning Zero Snapshot Documents from Firestore Listener 如何从 Firestore 文档获取更新? ReactJS - How can I get updates from a Firestore document? ReactJS 来自 Cloud Firestore 的 flutter Stream:如何将数据从快照复制到 class 并打印 - flutter Stream from Cloud Firestore : how to copy data from snapshot to class and print 使用 Firestore 模拟器时无法从客户端应用写入 Firestore - Cannot write to Firestore from client app when using Firestore Emulator 如何使用 StreamProvider 检查 Firestore 快照 stream 的 ConnectionState? - How check the ConnectionState of Firestore snapshot stream with StreamProvider? 随机播放 JavaScript 中 firebase Firestore 集合中的文档 - Shuffle docs from firebase firestore collection in JavaScript
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM