AJAX,全称是 Asynchronous JavaScript and XML,也就是异步的JavaScript和XML,是一种通过JavaScript在后台与服务器通信、实现网页局部更新和无刷新交互的技术思想。它是一组Web编程技术的组合,而不是某种单一的技术或者某种编程语言。


要详细了解AJAX,首先需要弄清楚以下几个问题:

什么是异步通信?它和同步通信有什么区别?

想象一下这样一个场景:你在浏览某个网页,在某个表单上提交了一些信息或者在点击了某个下拉的选项框,然后网页卡住了。你不能用滚轮拖动这个网页,也不能点击任何东西,直到你提交的东西传上去或者这个下拉框出现详细选项。出现这种情况的原因就是因为网页采取了同步通信的实现方式,而刚刚很不幸你的网络有些波动。

通过上面这个经历,我们对同步通信的流程有了一定的认知:

用户发起通信 ——> 一直等待 ——> 通信完成,获得结果 ——> 执行后面的代码

可以发现,当我们的网络因为某种原因突然变慢、网络请求无法完成,我们就不能对网页做任何操作,这在用户体验的角度上讲是致命的。

而异步通信的流程是这样的:

用户发起通信 ——> 先不管通信,执行后续的代码 ——> 等到了通信结果 ——> 执行回调,处理结果

它和同步通信最大的区别在于,异步通信不会阻塞用户操作。当用户发起通信后,用户依然可以浏览网页其它地方的内容,直到通信处理完成,或者等到用户突然发现家里的网欠费了。

至于为什么同步通信会因为网络原因被阻塞——因为JavaScript在浏览器中是单线程的,采用异步通信,浏览器就可以在后台发送请求,在前端继续响应用户的操作。


为什么是JavaScript?别的语言不可以吗?

AJAX是一种浏览器环境下的技术,当这种异步通信思想出现时(2005年),浏览器中唯一被所有平台原生支持、能直接操作页面、能发起网络通信的语言就是JavaScript。而别的语言想运行在浏览器环境下,要么兼容性不行,要么依赖插件,要么安全性不好,要么以上都占。所以自然而然的,JavaScript成为了实现AJAX的指定语言。


什么是XML?它和HTML有什么区别?它和现在的JSON有什么区别?

XML是Extensible Markup Language,中文名是“可扩展标记语言”,用来描述数据结构,标签由开发者进行定义,使用时需要由应用程序进行解释。

<user>
  <name>张三</name>
  <age>25</age>
  <hobbies>
    <hobby>读书</hobby>
    <hobby>跑步</hobby>
  </hobbies>
</user>

HTML是Hyper Text Markup Language,中文名是“超文本标记语言”,用来展示网页,标签是预定义的,浏览器知道如何渲染它们。

<!DOCTYPE html>
<html>
<head><title>示例</title></head>
<body>
  <h1>标题</h1>
  <p>段落<br>
  <img src="a.png" alt="图">
</body>
</html>

JSON是JavaScript Object Notation,中文名是“JavaScript对象表示法”,常用键值对和数组来表示数据,支持数据类型区分,能够方便地转化为JavaScript对象。

{
  "name": "张三",
  "age": 25,
  "hobbies": ["读书", "跑步"]
}

在早期的AJAX中,返回的数据是XML形式的,而相同数据下JSON使用更少的字符、数据类型清晰、结构紧凑,在技术演化中逐渐取代了XML,成为AJAX中主流的返回数据类型。


我们已经了解了AJAX,它是一种用JavaScript实现的异步通信技术,在通信时采用JSON作为返回数据类型。接下来我们的问题是:

如何实现AJAX?

这是一门成熟的技术,古早的方法有XMLHttpRequest,JavaScript原生的方法有fetch API,第三方库有axios。我们以fetch API为例,讲述一下如何实现AJAX。

fetch('/api/user')
  .then(res => res.json())
  .then(data => {
    document.getElementById('name').innerText = data.name;
  });

这是一个简短的AJAX示例代码,其中:

fetch('/api/user')

它负责向同域名下的/api/user路径发一条GET请求,在发起请求时它会返回一个Promise,当服务器的响应体抵达后,它会将Promise兑现为Response。

fetch()的默认方法是GET,也可以通过指定method参数来发送POST请求。

.then(res => res.json())

res是上一步fetch返回的Response对象,res => res.json()是箭头函数的写法,等价于

res => {
  return res.json();
}

也等价于

function(res) {
  return res.json();
}

res.json()负责将服务器的响应体解析为JSON。但是服务器的响应包不是一块到达的,先抵达的是响应头,res.json()收到响应头后也会返回一个Promise,当响应体解析完成之后,这个Promise会兑现为JSON。这个过程同样是异步的,当解析完成之后第二个.then才会执行回调。

.then(data => {
    document.getElementById('name').textContent = data.name;
 });

data是上一步res.json()解析得到的JavaScript对象。假如JSON数据是

{
  "name": "张三",
  "age": 25
}

那么得到的JavaScript对象就是

{
 "name": "张三", "age": 25}

最后

document.getElementById('name').innerText = data.name

负责按id查找DOM结构,将id = name的标签中的文字替换为张三。最终可以得到:

<span id="name">张三</span>

一次完整的AJAX请求流程为:

时间轴:

发起 fetch
    |
    |  Promise 处于 pending
    |
服务器开始响应
    |
    |  响应头到达
    |  Promise 变成 fulfilled,得到 Response 对象
    |  此时可以访问 res.status、res.headers
    |
    |  响应体还在传输中...
    |
    |  调用 res.json(),返回一个新的 Promise
    |  这个新 Promise 等待响应体读完并解析
    |
响应体读完并解析完成
    |
    |  新 Promise 变成 fulfilled,得到 JavaScript 对象
    |  此时可以访问 data.name

什么是Promise和Response?

我们在上文中多次提到了这两个名词,接下来我们解释一下它们。

  • Promise:

它代表一个“将来才会知道结果”的值。你可以理解为在餐厅点餐后领取到的号码牌,领到号之后你可以继续做别的事,等到叫号之后再去取餐。

Promise的初始状态是pending,表示等待状态;

Promise的成功状态是fulfilled,表示有处理结果了,交给.then()处理;

Promise的失败状态是rejected,表示出了问题,交给.catch()处理。

  • Response:

它代表一次HTTP响应的完整信息,包含状态码、状态文本、响应头、响应体。

其中,当响应头抵达时,fetch的Promise从pending改成fulfilled并兑现Response。此时可以根据响应头的内容来决定是否继续处理响应体。

Response的常用属性有:

res.ok          // 布尔值,状态码在 200-299 之间时为 true
res.status      // 数字,如 200、404
res.statusText  // 字符串,如 "OK"
res.headers     // 响应头对象
res.url         // 实际请求的 URL

我们的示例代码还可以改进一下:

fetch('/api/user',{
  headers: {  // 加入请求头
    'Content-Type': 'application/x-www-form-urlencoded'
  }
})
  .then(res => {
    if (!res.ok) {  // 检查状态码并抛出错误状态
      throw new Error(`HTTP ${res.status}`);
    }
    return res.json();
  })
  .then(data => {
    document.getElementById('name').innerText = data.name;
  })
  .catch(err => {  // 异常处理机制
    console.error('请求失败:', err);
  });

以上就是本文的全部内容,如有谬误,欢迎在评论区指正。