为什么这些获取方法是异步的
Why are these fetch methods asynchronous?
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));
为什么处理响应的有效负载返回一个承诺-我不清楚为什么它应该是一个异步操作。
为什么这些获取方法是异步的?
最基本的答案是"因为规范这么说"
arrayBuffer()
方法,当被调用时,必须返回使用ArrayBuffer运行消费体的结果。blob()
方法,当被调用时,必须返回使用Blob运行消费体的结果。formData()
方法,当调用时,必须返回使用FormData运行消费体的结果。json()
方法,当调用时,必须返回使用JSON运行消费体的结果。text()
方法,当被调用时,必须返回带有文本的消费体运行的结果。
当然,这并没有真正回答问题,因为它留下了一个问题:"为什么规范这么说?"
这就是它变得复杂的地方,因为我确定推理,但我没有来自官方来源的证据来证明它。我将尽我所能解释其中的原因,但请注意,在此之后的一切都应被视为外部意见。
当您使用fetch API从资源请求数据时,您必须等待资源完成下载才能使用它。这应该是相当明显的。JavaScript使用异步api来处理此行为,以便所涉及的工作不会阻塞其他脚本,更重要的是阻塞UI。
资源下载完成后,数据可能非常大。没有什么可以阻止您请求超过50MB的单一JSON对象。
如果你试图同步解析50MB的JSON,你认为会发生什么?它会阻止其他脚本,更重要的是会阻止UI。 其他程序员已经解决了如何以高性能的方式处理大量数据:Streams。在JavaScript中,流是使用异步API实现的,这样它们就不会阻塞,如果你阅读消费体的详细信息,很明显流是用来解析数据的:如果body为非空,则stream为body的流,否则为空ReadableStream对象。
现在,规范当然可以定义两种访问数据的方式:一个用于较小数据量的同步API,一个用于较大数据量的异步API,但这会导致混淆和重复。
除此之外,你不需要它。所有可以用同步代码表示的东西都可以用异步代码表示。反之则不然。因此,创建了可以处理所有用例的单个异步 API。
因为内容在你开始阅读之前是不会被传输的。头是第一位的。
看看这里的实现,获取json
的操作是CPU绑定的,因为响应的创建以及响应体是在响应承诺完成后完成的。参见json
函数的实现
话虽如此,我认为这主要是一个设计概念,所以你可以链接你的承诺处理程序,只使用一个错误处理程序,不管错误发生在哪个阶段。
:
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,但它可以编写更好看的代码。至少在我看来是这样。
在阅读了fetch的实现之后,似乎有几个原因使用promise。对于初学者来说,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)
}
})
}
从那里,额外的承诺被用来封装可能发生的特定错误,并将它们传播给调用者。例如,如果主体之前已经被读取过一次,如果blob没有转换为文本,如果文本没有转换为JSON,则可能会发生错误。承诺在这里很方便,因为这些不同的错误中的任何一个都会在调用者的catch块中结束。
所以在结论中,一个基于承诺的api被用于读取读取响应,因为:1. 它们依赖于必须异步初始化自身的FileReader
。2. Fetch想要传播在读取主体时可能发生的各种错误。
- 将其中一个异步方法重写为使用promise的方法
- strongloop script.js run find in before delete方法:如何同步运行异步方法
- QtWebKit中的JavaScript和异步方法
- Node.js-异步方法调用问题
- Meteor:如何遍历一个数组,这是一个异步方法的结果
- JavaScript中异步方法的优点是什么
- 如何将异步方法返回的值发送到javascript中的另一个异步方法
- 在javascript中链接多个异步方法
- 是否有可能在 JavaScript 中使用异步方法进行迭代
- 如何在 javascript 中同步异步方法
- for 循环中的异步方法
- 方法未按正确的顺序/异步方法调用
- 灯光开关加载所有数据或同步运行异步方法
- PHP 上的异步方法
- 如何确保异步方法已完成
- 如何测试React组件异步方法
- 如何在另一个异步`each`方法(NodeJS)内部进行异步方法调用
- 如何在angularjs中编写一个返回承诺的异步方法
- 如何在node js中返回异步方法调用的值
- node.js/express应用程序-使用哪个异步方法来替换我对HGET的嵌套调用