由于同步请求会阻塞调用过程,直到它们的结果被接收,所以在构建Web应用程序时应该只使用异步HTTP请求。然而,异步代码的编写和理解可能很棘手,因为语句不会像同步操作那样以线性和顺序的方式执行。
fetch()方法
JavaScript中发送异步HTTP请求的最佳方式是使用fetch()方法。下面是它的一般用法:
// Sends an asynchronous HTTP request to the target url
fetch(url)
.then(() => {
// Code called in the future when the request ends successfully
})
.catch(() => {
// Code called in the future when an errors occurs during the request
});
您可能会遇到使用一个名为 XMLHttoRequest 的对象来执行HTTP操作的JavaScript 代码。这是一种更古老的技术,现在已经被fetch()所取代。
Under the Hood : Promises
当 fetch()方法被执行时,它立即返回一个 promise,这是一个操作的包装,其结果可能在未来可用。promise处于以下状态之一:
- pending:初始状态,未完成或被拒绝
- fullfilled:表示操作成功完成
- rejected:操作失败
JavaScript是一个带有 then() 和 catch() 方法的对象。当 promise 实现时,调用 then()。它将操作结果作为参数。相反,当 promise 被拒绝时,会调用 catch()。
promise 的伟大之处在于它们可以被栓在一起。下面介绍如何在JavaScript 中执行一系列异步操作。
getData()
.then(a => filterData(a)) // Called asynchronously when getData() returns
.then(b => processData(b)) // Called asynchronously when filterData() returns
.then(c => displayData(c)) // Called asynchronously when processData() returns
示例:检索一个文本文件
让我们从一个非常基本的例子开始:显示位于 Web 服务器上的文本文件的内容。它包含以下内容:
C++;Java;C#;PHP
这里是如何在JavaScript 中使用 fetch()实现这一点:
fetch(
"https://raw.githubusercontent.com/bpesquet/thejsway/master/resources/languages.txt"
)
.then(response => response.text()) // Access and return response's text content
.then(text => {
console.log(text); // Display file content in the console
});
fetch()创建的异步HTTP请求的结果以 Response 对象的形式出现。这个对象有几个方法来处理HTTP调用的响应。本例中使用的 text()方法读取响应的文本内容并返回另一个 promise。它的结果由第二个 then() 方法管理,该方法只在控制台中显示文件的文本内容。
处理错误
从本质上讲,外部HTTP请求容易出现错误:网络故障、资源丢失等。处理这些错误的方法是在fetch()调用中添加一个catch()方法。错误处理的一个基本级别是在控制台中记录错误消息。
fetch("http://non-existent-resource")
.catch(err => {
console.error(err.message);
});
处理Json 数据
让我们进入一个更有趣和更现实的场景。很多时候,Web服务器上可用的数据都是以Json格式发布的。
Json 和 JavaScript
JavaScript 语言提供了对Json格式的原生支持:
- JSON.parse()方法将Json字符串转换为JavaScript对象。
- 相反,JSON.stringify()方法将JavaScript对象转换为JSON字符串。
// Define a JavaScript object
const plane = {
manufacturer: "Airbus",
model: "A320"
};
console.log(plane); // Display the object
const planeText = JSON.stringify(plane);
console.log(planeText); // Display the object as a JSON string
console.log(JSON.parse(planeText)); // Display the object
输出结果:
这些方法也可以处理JSON数组:
// Define an array containing two objects
const planes = [
{
manufacturer: "Airbus",
model: "A320"
},
{
manufacturer: "Boeing",
model: "737"
}
];
console.log(planes); // Display the array of objects
const planesText = JSON.stringify(planes);
console.log(planesText); // Display the array as a JSON string
console.log(JSON.parse(planesText)); // Display the array of objects
输出结果:
举例:检索JSON内容
例如,下面的JSON文件 movies.json包含有关某些电影的信息。该文件定义了一个包含三个对象的数组:
[ { "title": "The Wolf of Wall Street", "year": "2013", "author": "Martin Scorsese" }, { "title": "Inside Out", "year": "2015", "author": "Pete Docter" }, { "title": "Babysitting", "year": "2013", "author": "Philippe Lacheau and Nicolas Benamou" } ]
以下是如何从其URL中检索此文件并在控制台中显示每个电影标题:
fetch(
"https://raw.githubusercontent.com/bpesquet/thejsway/master/resources/movies.json"
)
.then(response => response.json()) // Access and return response's JSON content
.then(movies => {
// Iterate on the movie array
movies.forEach(movie => {
// Display title of each movie
console.log(movie.title);
});
})
.catch(err => {
console.error(err.message);
});
HTTP Response 对象返回一个promise,该 promise的解析结果为解析响应文本为JSON。因此,第二个 then() 的movies参数是一个可以迭代的普通JavaScript 数组。