【问题标题】:Turn basic polling into long polling将基本轮询变成长轮询
【发布时间】:2020-04-26 03:52:10
【问题描述】:

我正在开发一个聊天应用程序,到目前为止,我每两秒轮询一次服务器以获取消息。为了让聊天更即时,我想实现长轮询。我正在尝试为此实现JS.info Guide,但我一直没有达到我打算达到的目标。谁能针对我的情况指出这一点?

我在 react 中有一个自定义的钩子,现在是基本的轮询。

仅供参考:我是初学者,现在不会使用 WS 或 Socket.io。在这一点上对我来说太多了。

提前致谢!

export default function useGetChatMessages(_id) {
  const [chatMessages, setChatMessages] = React.useState([]);

  React.useEffect(() => {
    const interval = setInterval(() => {
      getChatMessages(_id).then(fetchedChatMessages => {
        setChatMessages(fetchedChatMessages);
      });
    }, 2000);
    return () => clearInterval(interval);
  }, [_id]);

  return chatMessages;
}

这是我使用 express 的服务器功能。我想它应该在这里实现而不是在我的钩子中实现

router.get('/:id/messages', async (request, response) => {
  try {
    const chat = await Chat.findById(request.params.id);
    response.json(chat.messages);
  } catch (error) {
    console.log(error);
    response.status(500).json({ message: error.message });
  }
});

【问题讨论】:

  • 我个人认为 WS 或 Socket.IO 比通过幅度轮询更容易实现

标签: javascript reactjs polling long-polling


【解决方案1】:

长轮询基本上是保持 HTTP 连接打开的一种方式,因此您需要在服务器端实现这一点,您的前端无法控制服务器对请求的处理。

作为旁注,长轮询通常比 websockets 对服务器的负担要大得多,老实说,你的时间最好花在实现 websocket 连接上,如果你使用这样的库,设置一个基本的 ws 服务器并不难作为socket.io

编辑:就像语义说明一样,长轮询是一种事件驱动的方式,服务器可以将响应发送回客户端,而无需客户端发送特定请求来请求该数据,而常规轮询只是在特定位置发送请求间隔(听起来您已经知道这一点,因为您提到希望让您的投票更加即时)。

如果您有兴趣改进常规投票设置,请查看其他答案。

【讨论】:

  • 你是绝对正确的特拉维斯。我现在已经在我的应用程序中实现了第一个 socket.io 代码,学习如此强大的东西真是太酷了。周末愉快
【解决方案2】:

长轮询也需要更改后端。所以假设你不能更改服务器端代码,你仍然可以实现更全面的基本 http 轮询。
要执行基本的 http 轮询,您必须在最后一次成功调用之后调用您的 API,因此您要确保只有一个 API 调用正在排队。

我还添加了一个粗略的Exponential Backoff 以更好地处理错误。如果您有后端问题(例如中断),这将防止您的前端锤击您的服务器。

修改后的代码如下所示:

export default function useGetChatMessages(_id) {
  const [chatMessages, setChatMessages] = React.useState([]);

  React.useEffect(() => {
    let delay = 1000;
    let timeout = null;

    const updateChat = () => {
      getChatMessages(_id).then(fetchedChatMessages => {
        // you must also consider passing a timestamp to the API call
        // so you only fetch the latest messages, instead of all of them every time
        // your state update would look like this:
        // setChatMessages((messages) => [...messages, fetchedChatMessages]);
        setChatMessages(fetchedChatMessages);
        // reset the delay in case an error has happened and changed it.
        delay = 1000;
        // now call the API again after 1 second
        timeout = setTimeout(updateChat, delay);
      }).catch(error => {
        // exponential backoff here.
        // 1 - the first error will call the API after 2sec
        // 2 - the second error will call the API after 4 sec
        // 3 - the third error will call the API after 8 sec
        // and so on
        console.error("Could not update chat. waiting a bit...", error);
        delay = delay * 2;
        timeout = setTimeout(updateChat, delay);
      });
    }

    return () => timeout && clearTimeout(timeout)

  }, [_id]);

  return chatMessages;
}

【讨论】:

  • 作为语义说明,这不是长轮询,它只是按设定的间隔定期轮询。长轮询是一种事件驱动的方式,服务器无需客户端发送特定请求即可向客户端发送回响应
  • 你是绝对正确的。这只是一个常规的 http 轮询。编辑了我的答案以包含它。
  • 嘿布鲁诺,感谢您如此努力地回答! :-) 不幸的是,这没有获取我的消息,并且我收到一条短消息,表明 updateChat 没有在任何地方调用。我尝试实施它,但没有奏效。我想我会继续花几个小时来学习 websockets,因为我绝对必须这样做才能扩展这个聊天应用程序。
  • 也许在我的原始帖子中发布的服务器路由中实现这一点会更好?
猜你喜欢
  • 2013-08-08
  • 2011-04-20
  • 2019-05-28
  • 1970-01-01
  • 2010-09-24
  • 2012-02-03
  • 2012-02-24
相关资源
最近更新 更多