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

时间:2016-08-26 16:13:09

标签: javascript fetch

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

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

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

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

  • arrayBuffer()
  • 团块()
  • FORMDATA()
  • JS​​ON()
  • 文本()

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

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 :(得分:1)

  

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

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

  
      
  • arrayBuffer()方法在调用时必须返回使用ArrayBuffer运行consume body的结果。
  •   
  • blob()方法在调用时必须返回使用Blob运行consume body的结果。
  •   
  • formData()方法在调用时必须返回使用FormData运行consume body的结果。
  •   
  • json()方法在调用时必须返回使用JSON运行consume body的结果。
  •   
  • text()方法在调用时,必须返回运行consume body文本的结果。
  •   

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

这就是它变得复杂的地方,因为我确定了理由,但我没有来自官方消息来证明它的证据。我将尽力解释理性,但请注意,此后的所有内容都应被视为外部意见。

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

资源下载完成后,数据可能会很大。没有任何东西可以阻止您请求超过50MB的单片JSON对象。

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

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

  

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

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

查看实现here,获取json的操作受CPU约束,因为响应创建完成后,响应的创建以及正文将完成。请参阅json function

的实施

话虽如此,我认为这主要是一个设计概念,因此您可以链接您的承诺处理程序,并且只使用一个启动的错误处理程序,无论错误发生在哪个阶段。

像这样:

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,但它可以创建更好看的代码。至少在我看来。

答案 3 :(得分: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) } }) } ,因此承诺链的起始位置。

FileReader

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

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

相关问题