首页 文章

为什么这些fetch方法是异步的?

提问于
浏览
4

Fetch是用于发出网络请求的新的基于Promise的API:

fetch('https://www.everythingisawesome.com/')
  .then(response => console.log('status: ', response.status));

这对我来说很有意义 - 当我们发起网络呼叫时,我们返回一个Promise,让我们的线程继续进行其他业务 . 当响应可用时,Promise中的代码将执行 .

但是,如果我对响应的有效负载感兴趣,我会通过响应的方法,而不是属性:

  • arrayBuffer()

  • blob()

  • formData()

  • json()

  • text()

这些方法回报了承诺,我不明白为什么 .

fetch('https://www.everythingisawesome.com/') //IO bound
  .then(response => response.json()); //We now have the response, so this operation is CPU bound - isn't it?
  .then(entity => console.log(entity.name));

为什么处理响应的有效负载会返回一个承诺 - 我不清楚为什么它应该是异步操作 .

4 回答

  • 0

    为什么这些fetch方法是异步的?

    天真的答案是"because the specification says so"

    调用arrayBuffer()方法时,必须返回使用ArrayBuffer运行使用体的结果 . 调用时,blob()方法必须返回使用Blob运行使用体的结果 . 调用formData()方法时,必须返回使用FormData运行使用body的结果 . 调用时,json()方法必须返回使用JSON运行使用体的结果 . 调用text()方法时,必须返回运行带有文本的使用体的结果 .

    当然,这并没有真正回答这个问题,因为它留下了“为什么规范这么说?”的问题 .

    这就是它变得复杂的地方,因为我确定了这个推理,但我没有来自官方消息来证明它的证据 . 我将尝试尽我所能地解释理性,但请注意,此后的所有内容应主要视为外部意见 .


    使用fetch API从资源请求数据时,必须等待资源完成下载才能使用它 . 这应该是相当明显的 . JavaScript使用异步API来处理此行为,以便所涉及的工作不会阻止其他脚本,更重要的是阻止UI .

    资源下载完毕后, the data might be enormous . 没有什么可以阻止您请求超过50MB的单片JSON对象 .

    如果您试图同步解析50MB的JSON,您认为会发生什么?它会阻止其他脚本,更重要的是阻止UI .

    其他程序员已经解决了如何以高效的方式处理大量数据:Streams . 在JavaScript中,流是使用异步API实现的,因此它们不会阻塞,如果您阅读consume body详细信息,很明显流正在用于解析数据:

    如果body为非null,则将stream设置为body的流,否则设为空的ReadableStream对象 .

    现在,规范可能已经定义了两种访问数据的方式:一种是用于少量数据的同步API,另一种是用于大量数据的异步API,但这会导致混淆和重复 .

    除了Ya Ain't Gonna Need It . 可以使用同步代码表达的所有内容都可以用异步代码表示 . 反之则不然 . 因此,创建了一个可以处理所有用例的异步API .

  • 0

    因为在您开始阅读之前不会传输内容 . Headers 首先出现 .

  • 0

    查看实现here获取 json 的操作是CPU绑定的,因为响应创建后,响应的创建以及正文都会完成 . 查看json function的实现

    话虽这么说,我认为这主要是一个设计概念,所以你可以链接你的promise处理程序,并且只使用一个启动的错误处理程序,无论错误发生在什么阶段 .

    像这样:

    fetch('https://www.everythingisawesome.com/')
      .then(function(response) {
        return response.json()
      })
      .then(function(json) {
        console.log('parsed json', json)
      })
      .catch(function(ex) {
        console.log('parsing or loading failed', ex)
      })
    

    已经解决的承诺的创建以非常低的开销实现 . 最后,这里不需要使用promise,但它可以创建更好看的代码 . 最起码,我是这么想的 .

  • 0

    在阅读了fetch的实现之后,似乎使用了promises有几个原因 . 对于初学者, json() 依赖于 FileReader 将响应blob转换为文本 . FileReaders 直到 onload 回调才能使用,因此这是承诺链开始的地方 .

    function fileReaderReady(reader) {
      return new Promise(function(resolve, reject) {
        reader.onload = function() {
          resolve(reader.result)
        }
        reader.onerror = function() {
          reject(reader.error)
        }
      })
    }
    

    从那里,额外的promise用于封装可能发生的特定错误并将它们传播到调用者 . 例如,如果正文可能会发生错误如果blob没有转换为文本,并且文本没有转换为JSON,则之前已经读过一次 . Prom在这里很方便,因为这些各种错误中的任何一个都会简单地结束在调用者的catch块中 .

    总而言之,基于承诺的api用于读取获取响应,因为:1 . 它们依赖于 FileReader ,它必须异步初始化自身 . 2. fetch想传播读取身体时可能出现的各种错误 . 承诺允许统一的方式来做到这一点 .

相关问题