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 回答
天真的答案是"because the specification says so"
当然,这并没有真正回答这个问题,因为它留下了“为什么规范这么说?”的问题 .
这就是它变得复杂的地方,因为我确定了这个推理,但我没有来自官方消息来证明它的证据 . 我将尝试尽我所能地解释理性,但请注意,此后的所有内容应主要视为外部意见 .
使用fetch API从资源请求数据时,必须等待资源完成下载才能使用它 . 这应该是相当明显的 . JavaScript使用异步API来处理此行为,以便所涉及的工作不会阻止其他脚本,更重要的是阻止UI .
资源下载完毕后, the data might be enormous . 没有什么可以阻止您请求超过50MB的单片JSON对象 .
如果您试图同步解析50MB的JSON,您认为会发生什么?它会阻止其他脚本,更重要的是阻止UI .
其他程序员已经解决了如何以高效的方式处理大量数据:Streams . 在JavaScript中,流是使用异步API实现的,因此它们不会阻塞,如果您阅读consume body详细信息,很明显流正在用于解析数据:
现在,规范可能已经定义了两种访问数据的方式:一种是用于少量数据的同步API,另一种是用于大量数据的异步API,但这会导致混淆和重复 .
除了Ya Ain't Gonna Need It . 可以使用同步代码表达的所有内容都可以用异步代码表示 . 反之则不然 . 因此,创建了一个可以处理所有用例的异步API .
因为在您开始阅读之前不会传输内容 . Headers 首先出现 .
查看实现here获取
json
的操作是CPU绑定的,因为响应创建后,响应的创建以及正文都会完成 . 查看json function的实现话虽这么说,我认为这主要是一个设计概念,所以你可以链接你的promise处理程序,并且只使用一个启动的错误处理程序,无论错误发生在什么阶段 .
像这样:
已经解决的承诺的创建以非常低的开销实现 . 最后,这里不需要使用promise,但它可以创建更好看的代码 . 最起码,我是这么想的 .
在阅读了fetch的实现之后,似乎使用了promises有几个原因 . 对于初学者,
json()
依赖于FileReader
将响应blob转换为文本 .FileReaders
直到onload
回调才能使用,因此这是承诺链开始的地方 .从那里,额外的promise用于封装可能发生的特定错误并将它们传播到调用者 . 例如,如果正文可能会发生错误如果blob没有转换为文本,并且文本没有转换为JSON,则之前已经读过一次 . Prom在这里很方便,因为这些各种错误中的任何一个都会简单地结束在调用者的catch块中 .
总而言之,基于承诺的api用于读取获取响应,因为:1 . 它们依赖于
FileReader
,它必须异步初始化自身 . 2. fetch想传播读取身体时可能出现的各种错误 . 承诺允许统一的方式来做到这一点 .