You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
setTimeout(callback, 10000);
function callback() {
console.log("hello timer!");
}
XMLHttpRequest 基于 DOM 事件,处理程序回调注册在 onload 对象上。当请求成功时,load 事件触发。
"use strict";
const request = new XMLHttpRequest();
request.onload = callback;
function callback() {
console.log("Got the response!");
}
注册回调之后,我们可以使用 open() 打开请求。它接受一个 HTTP 方法
"use strict";
const request = new XMLHttpRequest();
request.onload = callback;
function callback() {
console.log("Got the response!");
}
request.open("GET", "https://academy.valentinog.com/api/link/");
最后,我们可以使用 send() 发送实际的请求
"use strict";
const request = new XMLHttpRequest();
request.onload = callback;
function callback() {
console.log("Got the response!");
}
request.open("GET", "https://academy.valentinog.com/api/link/");
request.send();
在浏览器中打开 build-list.html,在控制台中会看到“Got the response!”,说明请求成功。如果你还记得第6章,每个常规 JS 函数都有一个对其宿主对象的引用。因为回调在 XMLHttpRequest 对象中运行,所以可以通过 this.response 获取服务器返回的数据。
"use strict";
const request = new XMLHttpRequest();
request.onload = callback;
function callback() {
// this refers to the new XMLHttpRequest
// response is the server's response
console.log(this.response);
}
request.open("GET", "https://academy.valentinog.com/api/link/");
request.send();
"use strict";
const request = new XMLHttpRequest();
request.onload = callback;
function callback() {
// this refers to the new XMLHttpRequest
// response is the server's response
console.log(this.response);
}
// configure the response type
request.responseType = "json";
//
request.open("GET", "https://academy.valentinog.com/api/link/");
request.send();
function fetch(url, requestInit) {
return new Promise(function(resolve, reject) {
const request = new XMLHttpRequest();
const requestConfiguration = new Request(requestInit || {});
request.open(requestConfiguration.method, url);
request.onload = function() {
const response = new Response(this);
resolve(response);
};
request.onerror = function() {
reject("Network error!");
};
// Set headers on the request
for (const header in requestConfiguration.headers) {
request.setRequestHeader(header, requestConfiguration.headers\[header\]);
}
// If there's a body send it
// If not send an empty GET request
requestConfiguration.body && request.send(requestConfiguration.body);
requestConfiguration.body || request.send();
});
}
阿里云最近在做活动,低至2折,真心觉得很划算了,可以点击本条内容或者链接进行参与:
https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=pxuujn3r
REST API 和 XMLHttpRequest
如果你问我,我会说 JS 挺强大的。作为一种在浏览器中运行的脚本语言,它可以做所有类似的事情:
等等。在第
8
章中,咱们从数组开始构建了一个 HTML 表格。 硬编码数组是一个同步数据源,也就是说,可以直接在咱们的代码中使用它,无需等待。 但是大多数时候,数据都是从后台请求过来的。网络请求始终是异步操作,而不是同步数据源:请求数据,服务器会有一定的延迟后才响应。JS 本身没有内置的异步性:它是“宿主”环境(浏览器或 Node.j),为处理耗时的操作提供了外部帮助。在第3章中,咱们看到了
setTimeout
和setInterval
,这两个属于Web API
的。浏览器提供了很多 API,其中还有一个叫XMLHttpRequest
,专门用于网络请求。事实上,它来自于以 XML 数据格式的时代。现在
JSON
是最流行的用于在 Web 服务之间移动数据的通信“协议”,但XMLHttpRequest
这个名称最终被保留了下来。XMLHttpRequest
也是AJAX
技术的一部分,它是 “异步JavaScript和XML” 的缩写。AJAX 就是为了在浏览器中尽可能灵活地处理网络请求而诞生的。它的作用是能够从远程数据源获取数据,而不会导致页面刷新。当时这个想法几乎是革命性的。随着 XMLHttpRequest (大约13年前)的引入,咱们可以使用它来进行异步请求。在上述的示例中:
创建一个新的
XMLHttpRequest
对象通过提供方法和网址来打开请求
注册事件监听器
发送请求
XMLHttpRequest 是基于DOM事件的,咱们可以使用
addEventListener
或onload
来监听“load
”事件,该事件在请求成功时触发。对于失败的请求(网络错误),咱们可以在“error
”事件上注册一个侦听器:有了这些知识,咱们就更好地使用
XMLHttpRequest
。通过 XMLHttpRequest 请求数据,构建 HTML 列表
从 REST API 提取数据后,咱们将构建一个简单的 HTML 列表。 新建一个名为 build-list.html 的文件:
接下来,在同一个文件夹中创建一个名为 xhr.js 的文件。在这个文件中,创建一个新的 XHR 请求:
上面的调用(构造函数方式)创建了一个
XMLHttpRequest
类型的新对象。与setTimeout
等异步函数相反,我们把回调作为参数:XMLHttpRequest
基于 DOM 事件,处理程序回调注册在onload
对象上。当请求成功时,load
事件触发。注册回调之后,我们可以使用
open()
打开请求。它接受一个HTTP
方法最后,我们可以使用
send()
发送实际的请求在浏览器中打开 build-list.html,在控制台中会看到
“Got the response!”
,说明请求成功。如果你还记得第6章,每个常规 JS 函数都有一个对其宿主对象的引用。因为回调在XMLHttpRequest
对象中运行,所以可以通过this.response
获取服务器返回的数据。保存文件并刷新 build-list.html。在控制台可以看到返回的数据,数据格式是字符串,有两种方法可以把它变成 JSON 格式:
方法一:在
XMLHttpRequest
对象上配置响应类型方法二:使用
JSON.parse()
方法一:
方法二 比较推荐,也符合咱们现在的编程习惯:
再次刷新build-list.html,会看到一个 JS 对象数组,每个对象都具有相同的结构:
这次,咱们没有像第8章那样手工创建数组,而是通过 REST API 接口请求数据。
使用 JS 构建 HTML 列表(和调试类)
这里咱们使用 ES6 类的方法来构建,还会使用私有类字段(在撰写本文时,Firefox不支持该字段)。在编写任何代码之前,都要思考一下,别人会“如何使用我的类”? 例如,另一个开发人员可以使用咱们的代码并通过传入来调用该类:
用于获取数据的 URL
要将列表附加到的 HTML 元素
const url = "https://academy.valentinog.com/api/link/";
const target = document.body;
const list = new List(url, target);
有了这些要求,咱们就可以开始编写类代码了。目前,它应该接受构造函数中的两个参数,并拥有一个获取数据方法
软件开发中的普遍观点是,除非有充分的理由要做相反的事情,否则不能从外部访问类成员和方法。 在 JS 中,除非使用模块,否则没有隐藏方法和变量的原生方法(第2章)。 即使是
class
也不能幸免于信息泄漏,但是有了私有字段,就能大概率避免这类问题。 JS 私有类字段的目前还没有成标准,但大部分浏览器已经支持了,它用#
来表示,重写上面的类:你可能不喜欢语法,但是私有类字段可以完成其工作。 这种方式,我们就不能从外部访问
url
和target
:有了这个结构,咱们就可以将数据获取逻辑移到
getData
中。现在,为了显示数据,咱们在
getData
之后添加一个名为render
的方法。render
将为我们创建一个 HTML 列表,从作为参数传递的数组开始:注意
document.createElement()
、document.createTextNode()
和appendChild()
。咱们在第8章讲DOM 操作的时候见过。this.#target
私有字段将 HTML 列表附加到 DOM。现在,我想:获取 JSON 响应后调用
render
当用户创建一个新的列表“实例”时立即调用
getData
为此,咱们在
request.onload
回调内部调用render
:另一方面,
getData
应该在构造函数中运行:完整代码:
尝试一下:在浏览器中刷新 build-list.html 并查看控制台
this.render
不是函数! 会是什么呢? 此时,你可能想要达到第6章或更高版本,可以调试代码。 在getData
中的this.render(response)
之后,添加debugger
指令:debugger
加了一个所谓的断点,执行将停止在那里。现在打开浏览器控制台并刷新build-list.html。下面是将在 Chrome 中看到的:仔细查看“Scopes”选项卡。
getData
中确实有一个this
,但它指向XMLHttpRequest
。 换句话说,我们试图在错误的对象上访问this.render
。为什么
this
不匹配? 这是因为传递给request.onload
的回调在 XMLHttpRequest 类型的宿主对象中运行,调用const request = request = new XMLHttpRequest()
的结果。解决方法,在前几章中已经提到过了,可以使用箭头函数
。刷新 build-list.html 并检查它
很好,前面的错误消失了,但是现在
JSON.parse
出现了一个问题。我们很容易想象它与this
有关。将debugger
向上移动一行刷新build-list.html并在浏览器控制台中再次查看 Scopes 。
response
是undefined
,因为我们要访问的this
是 List。这与箭头函数和类的行为一致(类默认为严格模式)。那么现在有什么解决办法吗?从第8章 DOM 和 events 中了解到,作为事件监听器传递的每个回调都可以访问
event
对象。在该event
对象中还有一个名为target
的属性,指向触发事件的对象。吃准可以通过event.target.response
获取响应回来的数据。完整代码:
接着,继续探索
XMLHttpRequest
的发展:Fetch
。异步演变:从 XMLHttpRequest 到 Fetch
Fetch API 是一种用于发出
AJAX
请求的原生浏览器方法,它常常被诸如Axios
之类的库所忽视。Fetch 与ES6 和新的Promise
对象一起诞生于 2015 年。另一方面,AJAX 从 1999 年开始就有了一套在浏览器中获取数据的技术。现在我们认为 AJAX 和 Fetch 是理所当然的,但是很少有人知道
Fetch
只不过是XMLHttpRequest
的 “美化版
”。Fetch
比典型的XMLHttpRequest
请求更简洁,更重要的是基于Promise
。这里有一个简单的事例:如果在浏览器中运行它,控制台将打印一个响应对象。根据请求的内容类型,需要在返回数据时将其转换为JSON
与你可能认为的相反,仅仅调用并没有返回实际的数据。由于
response.json()
也返回一个Promise
,因此需要进一步才能获得 JSON 有效负载:Fetch
比XMLHttpRequest
更方便、更干净,但它有很多特性。例如,必须特别注意检查响应中的错误。在下一节中,咱们将了解关于它的更多信息,同时从头重新构建Fetch
。从头开始重新构建 Fetch API
为了更好的理解 Fetch 原理,咱们重写
fetch
方法。首先,创建一个名为fetch.html的新文件,内容如下:然后在相同的文件夹中创建另一个名为 fetch.js 的文件,内容如下:
在第一行中,咱们确保处于严格模式,在第二行中,“取消”原始的Fetch API。现在咱们可以开始构建自己的 Fetch API 了。
fetch
的工作方式非常简单。它接受一个url
并针对它发出一个GET
请求:当带有
then
的函数说明该函数是“可链”的,这意味着它返回一个Promise
。因此,在 fetch.js 中,咱们创建一个名为fetch
的函数,它接受一个url
并返回一个新的Promise
。创建 Promise,可以调用Promise
构造函数,并传入一个回调函数来解析和拒绝:完善代码:
在控制台中得到“Fake response!” 。当然,这仍然是一个无用的
fetch
,因为没有从 API 返回任何东西。让咱们在 XMLHttpRequest 的帮助下实现真正的行为。咱们已经知道了 XMLHttpRequest 创建请求方式。接着,将XMLHttpRequest
封装到咱们的 Promise 中被拒绝的 Promise 由
catch
处理:现在,如果
url
是错误的,会打印具体的错误信息到控制台。如果url
正确,则打印请求到数据:上述实现方式还不够完善。首先,咱们需要实现一个返回
JSON
的函数。实际的 Fetch API 生成一个响应,可以稍后将其转换为 JSON、blob 或 文本,如下所示(对于本练习的范围,我们只实现 JSON 函数)实现该功能应该很容易。 似乎 “
response
” 可能是一个单独带有json()
函数的实体。 JS 原型系统非常适合构建代码(请参阅第5章)。 咱们创建一个名为 Response 的构造函数和一个绑定到其原型的方法(在fetch.js中):就这样,咱们我们可以在 Fetch 中使用 Response:
上面的代码在浏览器的控制台中打印一个对象数组。现在咱们来处理误差。Fetch 的真实版本比我们的
polyfill
复杂得多,但是实现相同的行为并不困难。Fetch 中的响应对象有一个属性,一个名为**“ok”**的布尔值。该布尔值在请求成功时设置为true
,在请求失败时设置为false
。开发人员可以通过引发错误来检查布尔值并更改Promise
处理程序。 这是使用实际Fetch
检查状态的方法:如你所见,还有一个
"statusText"
。 在 Response 对象中似乎容易实现"ok"
和"statusText"
。 当服务器响应成功,response.ok
为true
:重构
Response
方法,如下所示:这里不需要创建 "
statusText
",因为它已经从原始 XMLHttpRequest 响应对象返回了。这意味着在构造自定义响应时只需要传递整个响应但是现在咱们的 polyfill 有问题。 它接受单个参数 "
url
",并且仅对其发出 GET 请求。修复这个问题应该很容易。首先,我们可以接受第二个名为requestInit
的参数。然后根据该参数,我们可以构造一个新的请求对象:body
、method
和headers
首先,创建一个带有一些名为
body
,method
和headers
的属性的新Request
函数,如下所示:但在此之上,我们可以为设置请求头添加一个最小的逻辑
setRequestHeader
可以在 XMLHttpRequest 对象上直接使用。headers
对于配置 AJAX 请求很重要。 大多数时候,你可能想在headers
中设置application/json
或身份验证令牌。body
提供的下面是完整的代码:
真正的 Fetch API 实现要复杂得多,并且支持高级特性。我们只是触及了表面。可以改进代码,例如,添加
headers
的逻辑可以独立存在于方法上。此外,还有很大的空间可以添加新特性:支持 PUT 和 DELETE 以及更多以不同格式返回响应的函数。如果你想看到更复杂的获取 API polyfill,请查看来自 Github的 工程师的 whatwg-fetch。你会发现与咱们的
polyfill
有很多相似之处。总结
AJAX 让我们有机会构建流畅的、用户友好的界面,从而改变了我们构建 web 的方式。经典页面刷新的日子已经一去不复返了。
现在,咱们可以构建优雅的 JS 应用程序并在后台获取所需的数据。XMLHttpRequest 是用于发出HTTP 请求的优秀的旧遗留的 API,今天仍在使用,但其形式有所不同: Fetch API。
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具Fundebug。
原文:https://github.com/valentinogagliardi/Little-JavaScript-Book/blob/v1.0.0/manuscript/chapter9.md
交流
阿(a)里(li)云(yun)最近在做活动,低至2折,有兴趣可以看看:https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=pxuujn3r
干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
因为篇幅的限制,今天的分享只到这里。如果大家想了解更多的内容的话,可以去扫一扫每篇文章最下面的二维码,然后关注咱们的微信公众号,了解更多的资讯和有价值的内容。
每次整理文章,一般都到2点才睡觉,一周4次左右,挺苦的,还望支持,给点鼓励
The text was updated successfully, but these errors were encountered: