问题
如果你一直在浏览器或现代版本的 Node.js 中使用该函数,您就会知道它会返回一个 Promise。fetch()
fetch(request).then((response) => { // Handle the response. })
你还知道,如果 JavaScript 中的 promise 在解决时出现错误,则会拒绝它们。但是,由于某种不明原因,当我们收到错误响应时,返回的 Promise 永远不会被拒绝。这不仅令人困惑,而且还迫使您在进一步处理响应之前显式检查响应状态:fetch()
fetch(request).then((response) => { if (!response.ok) { throw new Error(`Server responded with ${response.statusCode}`) } return response.json() })
好的,在你把这个行为添加到 JavaScript 怪异的列表中之前,让我向你保证它是完全正确的。你认为它是出乎意料的,仅仅是因为 fetch Promise 代表你认为它的作用。让我解释一下。
承诺
自然地,当请求返回 promise 时,我们希望其 fulfillment state 反映该请求的状态:
- 如果请求成功,则 Promise 将解析;
- 如果请求不成功(即失败),则 Promise 会拒绝。
而这正是发生的事情!不同之处在于,您可能会从网络代码的角度误解了“成功请求”实际上是什么。
网络代码
下面是一个请求/响应事务,以稍微简化的步骤列表表示:
- 在您的代码中触发请求;
- 请求标头将发送到服务器。请记住,我说的是 HTTP 消息标头,即:
GET https://kettanaito.com HTTP/1.0 accept: text/html;charset=UTF-8 # ...the rest of the request headers, excluding the body.
- 请求正文开始流式传输到服务器。
- 请求正文完成流式处理。
- 服务器发送响应标头。
- 响应正文完成流式传输到客户端。
从网络代码的角度来看,一旦请求被成功完整地发送到服务器(在上面的列表中传递了 #3),它就是成功的。虽然这有点违反直觉,但 request 的成功与你从服务器得到的响应类型无关。
这意味着 fetch()
函数返回的 Promise 实际上是一个请求 Promise,并且只有在请求本身失败时才会拒绝。
错误响应
请记住,错误响应并不表示请求失败。要使应用程序从服务器获取错误,它必须到达该服务器,这意味着它必须成功发送请求并返回某些内容。
这又一次是意图的混淆。作为开发人员,我们在发出请求时的目的是为该请求获得 “满意路径” 解决方案。如果我们获取 ,我们希望得到 puppies 的列表。其他任何事情都被认为是意外的,即错误。GET /puppies
但是 fetch()
不能真的假设这一点。考虑一下我们实际上期望的是 error 响应而不是 .突然之间,我们对响应的期望取决于上下文,而不是 fetch 应该关心的事情。200 OK
拒绝
尽管人们普遍认为 Promise 确实拒绝了,以下是时间:fetch()
- 请求构造错误;
- 网络错误(例如 DNS 查找失败、无法访问网络);
- 请求已中止。
而这些正是你在 request Promise 的结尾应该处理的情况:.catch()
fetch(request) .then((response) => { // Handle the response. }) .catch((error) => { // Handle request errors. })
等等,但这实际上很不错!如果请求成功,我们将继续在回调中处理其响应(无论它是什么),如果请求本身失败,我们可以在回调中捕获并处理该错误。.then()
.catch()
Fetch API 是 Web 上设计良好的 API 之一,正是像这样的细节表明了这一点。请记住,在你思考一些奇怪的事情之前,试着更深入地了解它,也许,过去的古怪会成为未来的发现。