【问题标题】:How to cancel http request properly in Node.js?如何在 Node.js 中正确取消 http 请求?
【发布时间】:2019-10-25 18:19:27
【问题描述】:

我需要在 Node.js 中实现一个可取消的客户端 HTTP 请求,而不使用外部库。我给出了一个 Promise 对象 - cancellationPromise - 当外部请求取消时它会被拒绝。这就是我知道我可能需要致电request.abort() 的方式。

问题是,只有当https.request 仍处于待处理状态且response 对象尚不可用时,我是否应该调用request.abort()

或者,即使我已经得到response 对象并且正在处理响应数据,我是否应该调用它,如下面的代码所示?在这种情况下,这会阻止更多response.on('data') 事件的发生吗?

  async simpleHttpRequest(url, oauthToken, cancellationPromise) {
    let cancelled = null;
    let oncancel = null;

    cancellationPromise.catch(error => { 
      cancelled = error; oncancel && oncancel(error) });

    try {
      const response = await new Promise((resolve, reject) => {
        const request = https.request(
          url.toString(), 
          { 
            method: 'GET', 
            headers: { 'Authorization': `Bearer ${oauthToken}` }        
          }, 
          resolve);

        oncancel = error => request.abort();
        request.on('error', reject);
        request.end();
      });

      if (cancelled) throw cancelled;

      // do I need "oncancel = null" here?
      // or should I still allow to call request.abort() while fetching the response's data?

      // oncancel = null;

      try {
        // read the response 
        const chunks = await new Promise((resolve, reject) => {
          response.on('error', reject);  
          const chunks = [];
          response.on('data', data => chunks.push(data));
          response.on('end', () => resolve(chunks));
        });

        if (cancelled) throw cancelled;
        const data = JSON.parse(chunks.join(''));
        return data;
      }
      finally {
        response.resume();
      }
    }
    finally {
      oncancel = null;
    }
  }

【问题讨论】:

  • 您应该只在得到响应之前中止请求。收到响应后,您应该根据响应来决定是要中止请求还是要成功响应,而不是立即中止请求。
  • @AkanshGulati,如果cancellationPromise 在我得到响应对象后被拒绝但我仍在接收它的数据块怎么办?

标签: javascript node.js httprequest cancellation


【解决方案1】:

这取决于你想通过中止请求来实现什么。

只是一点背景。 HTTP 1 无法“取消”它发送的请求,然后等待响应。您不能“回滚”您所做的请求。您需要一个分布式事务来执行此操作。 (Further reading.) 作为MDN developer document states:

如果请求已经发送,XMLHttpRequest.abort() 方法会中止请求。当一个请求被中止时,它的 readyState 被改变为 XMLHttpRequest.UNSENT (0) 并且请求的状态码被设置为 0。

基本上,您会阻止响应被您的应用程序处理。其他应用程序可能(如果您在发送给它后调用abort())无论如何都会完成其处理。

从问题的角度来看:

问题是,我是否应该仅在 https.request 仍处于挂起状态且响应对象尚不可用时才调用 request.abort()

TL.DR.:这仅从您的应用程序的角度来看很重要。当我浏览您的代码时,我认为它可以正常工作。

【讨论】:

  • 谢谢!不过仍然很好奇,万一我已经得到了response 对象并仍然调用request.abort,你知道这是否会阻止即将到来的response.on('data') 事件以获取待处理的响应数据?我假设是因为docs say “将请求标记为中止。调用它会导致响应中的剩余数据被丢弃并销毁套接字。” 我还没有验证自己.另外,在这种情况下,我还需要打电话给response.resume()吗?
  • 据我所知并没有严格规定,但在我过去的项目中,您所做的假设是正确的。也就是它停止了response.on(data)。但是我们删除了这些代码部分,因为它对我们没有任何好处。
  • 可以中止 HTTP GET 或 HEAD,但不能中止 POST 或 PUT。一旦调用者决定中止,他们可能不会期望他们的任何钩子被调用并承诺被拒绝,所以我会说总是中止。
  • 我在一定程度上同意你的看法。大多数情况下,HEADGET 不会更改数据。可悲的是,我看到他们违反了这一点的系统......
猜你喜欢
  • 1970-01-01
  • 2017-03-06
  • 2019-12-11
  • 2018-01-22
  • 1970-01-01
  • 2018-10-31
相关资源
最近更新 更多