代码之家  ›  专栏  ›  技术社区  ›  stkvtflw

中止此节点获取请求而不中止所有其他请求

  •  0
  • stkvtflw  · 技术社区  · 3 年前

    我找到的唯一方法是在 node-fetch 请求是使用 abort-controller :

    import AbortController from 'abort-controller'
    import fetch from 'node-fetch'
    
    const fetchWithTimeout = async (url, options) => {
      const controller = new AbortController()
      const timeout = setTimeout(() => {
        controller.abort()
      }, 10000)
      const response = await fetch(url, {
        signal: controller.signal,
        ...options
      })
      clearTimeout(timeout)
      return response.text()
    }
    

    除了这看起来很丑陋之外,还有一个我不知道如何回避的巨大问题: 这个 controller.abort() 中止所有当前请求,而不仅仅是一个。所以,如果我打电话 fetchWithTimeout() 同时进行10次,其中一次流产,然后所有其他人也流产。

    我用更尴尬的代码解决了这个问题:

    import AbortController from 'abort-controller'
    import fetch from 'node-fetch'
    
    const aborted = {}
    const fetchWithTimeout = async (url, options) => {
      const controller = new AbortController()
      const timeout = setTimeout(() => {
        aborted[url] = true
        controller.abort()
      }, 10000)
      try {
        const response = await fetch(url, {
          signal: controller.signal,
          ...options
        })
        clearTimeout(timeout)
        return response.text()
      } catch (e) {
        clearTimeout(timeout)
        if (aborted[url] || e?.type !== 'aborted') {
          delete aborted[url]
          throw e
        } else {
          return await fetchWithTimeout(url, options)
        }
      }
    }
    

    但这是错误和低效的。除了用替代方法替换节点获取之外,我还能对它做什么?

    0 回复  |  直到 3 年前
        1
  •  1
  •   eol    3 年前

    我试图重现的行为 controller.abort() 中止所有当前请求,但它似乎如预期那样工作,我怀疑这是中的一个错误 node-fetch

    我认为问题在于当您并行(大概)执行请求时,如何处理错误 Promise.all .如前所述 in the docs 它在任何输入promise拒绝或非promise抛出错误时立即拒绝,并将使用第一个拒绝消息/错误进行拒绝。

    你可能正在寻找 Promise.allSettled 相反,当它在之后解决时 全部的 其中一个承诺是相互独立履行或拒绝的。所以它可能看起来像这样:

    (async () => {
        const parallelRequests = [];
        for (let i = 0; i < 5; i++) {
            parallelRequests.push(fetchWithTimeout('http://some-url.com'));
        }
        const result = await Promise.allSettled(parallelRequests);
        // result will be an array holding status information about each promise and the resolved value or the rejected error
    })();
    

    为什么您的第二种方法适用于 Promise.all ,是指实际捕获并处理fetch调用中的错误。但请注意,如果出现未中止的错误,则会重新抛出该错误,无论其他请求如何,该错误都会再次导致立即拒绝。

        2
  •  1
  •   stkvtflw    3 年前

    我在各种节点获取库上花了太多时间。只需使用卷曲。它只是起作用。

    import { exec } from 'child_process'
    
    async function curl (url: string) {
      return await new Promise(async resolve => {
        const command = `curl "${url}" -L --connect-timeout 10 --max-time 12`
        exec(command, { maxBuffer: 1024 * 1024 * 10 }, (err, stdout, stderr) => {
          if (err) {
            console.error(err, stderr)
            resolve('')
          } else {
            resolve(stdout)
          }
        })
      })
    }
    
    export default curl
    
        3
  •  0
  •   Vitaly    2 年前
    1. 我认为你需要使用 await response.text()
    2. clearTimeout(timeout) 之后 等待响应.text() ,以前没有。