update: Ajax
This commit is contained in:
165
05-JavaScript基础:异步编程和Ajax/01-单线程和异步.md
Normal file
165
05-JavaScript基础:异步编程和Ajax/01-单线程和异步.md
Normal file
@@ -0,0 +1,165 @@
|
||||
## 单线程
|
||||
|
||||
JavaScript 语言和执行环境是**单线程**。即同一时间,只能处理一个任务。
|
||||
|
||||
具体来说,所谓单线程,是指 JS 引擎中负责解释和执行 JavaScript 代码的线程只有一个,也就是一次只能完成一项任务,这个任务执行完后才能执行下一个。所有的任务都**需要排队**。
|
||||
|
||||
**JS 为何要被设计为单线程呢**?原因如下:
|
||||
|
||||
- 首先是历史原因,在最初设计 JS 这门语言时,多进程、多线程的架构并不流行,硬件支持并不好。
|
||||
|
||||
- 其次是因为多线程的复杂性,多线程操作需要加锁,编码的复杂性会增高。
|
||||
|
||||
- 而且,如果多个线程同时操作同一个 DOM,在多线程不加锁的情况下,会产生冲突,最终会导致 DOM 渲染的结果不符预期。
|
||||
|
||||
所以,为了避免这些复杂问题的出现,JS 被设计成了单线程语言。
|
||||
|
||||
## 同步任务和异步任务
|
||||
|
||||
如果当前正在执行的任务很耗时,它就会**阻塞**其他正在排队的任务。为了解决这个问题,JS 在设计之初,将任务分成了两类:同步任务、异步任务。
|
||||
|
||||
- 同步任务:在**主线程**上排队执行的任务。只有前一个任务执行完毕,才能执行下一个任务。
|
||||
|
||||
- 异步任务:不进入主线程、而是进入**任务队列**(Event Queue)的任务。只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
|
||||
|
||||
```js
|
||||
console.log('同步任务');
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('异步任务');
|
||||
}, 1000);
|
||||
```
|
||||
|
||||
比如上面的代码里:第一行代码是同步任务,会**立即执行**;定时器里的回调函数是异步任务,需要等 1 秒后才会执行。
|
||||
|
||||
### 前端使用异步的场景
|
||||
|
||||
什么时候需要**等待**,就什么时候用异步。
|
||||
|
||||
- 定时任务:setTimeout(定时炸弹)、setInterval(循环执行)
|
||||
|
||||
- 事件绑定(比如说,按钮绑定点击事件之后,用户爱点不点。我们不可能卡在按钮那里,什么都不做。所以,应该用异步)
|
||||
|
||||
- 网络请求(含接口请求):ajax 请求、网络图片加载
|
||||
|
||||
- ES6 中的 Promise
|
||||
|
||||
现在的大部分软件项目,都是前后端分离的。后端生成接口,前端请求接口。前端发送 ajax 请求,向后端请求数据,然后**等待一段时间**后,才能拿到数据。这个请求过程就是异步任务。
|
||||
|
||||
### 接口调用的方式
|
||||
|
||||
js 中常见的接口调用方式,有以下几种:
|
||||
|
||||
- 原生 ajax、基于 jQuery 的 ajax
|
||||
- Promise
|
||||
- Fetch
|
||||
- axios
|
||||
|
||||
下一篇文章,我们重点讲一下接口调用里的 Ajax,然后在ES6语法中学习 **Promise**。在这之前,我们需要先了解同步任务、异步任务的事件循环机制。
|
||||
|
||||
### 多次异步调用的顺序
|
||||
|
||||
- 多次异步调用的结果,顺序可能不同步。
|
||||
|
||||
- 异步调用的结果如果**存在依赖**,则需要通过回调函数进行嵌套。
|
||||
|
||||
### 事件循环机制(重要)
|
||||
|
||||

|
||||
|
||||
执行顺序如下:
|
||||
|
||||
- 同步任务:进入主线程后,立即执行。
|
||||
|
||||
- 异步任务:会先进入 Event Table;等时间到了之后,再进入 Event Queue,然后排队(为什么要排队?因为同一时间,JS 只能执行一个任务)。比如说,`setTimeout(()=> {}, 1000)`这种定时器任务,需要等一秒之后再进入 Event Queue。
|
||||
|
||||
- 当主线程的任务执行完毕之后,此时主线程处于空闲状态,于是会去读取 Event Queue 中的任务队列,如果有任务,则进入到主线程去执行。
|
||||
|
||||
## 代码示例
|
||||
|
||||
掌握了上面的事件循环原理之后,我们来看几个例子。
|
||||
|
||||
### 举例 1
|
||||
|
||||
```js
|
||||
console.log(1);
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(2);
|
||||
}, 1000);
|
||||
console.log(3);
|
||||
console.log(4);
|
||||
```
|
||||
|
||||
打印结果:
|
||||
|
||||
```
|
||||
1
|
||||
3
|
||||
4
|
||||
2
|
||||
```
|
||||
|
||||
解释:先等同步任务执行完成后,再执行异步任务。
|
||||
|
||||
### 举例 2(重要)
|
||||
|
||||
如果我把上面的等待时间,从 1 秒改成 0 秒,你看看打印结果会是什么。
|
||||
|
||||
```js
|
||||
console.log(1);
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(2);
|
||||
}, 0);
|
||||
console.log(3);
|
||||
console.log(4);
|
||||
```
|
||||
|
||||
打印结果:
|
||||
|
||||
```
|
||||
1
|
||||
3
|
||||
4
|
||||
2
|
||||
```
|
||||
|
||||
可以看到,打印结果没有任何变化,这个题目在面试中经常出现,考的就是 `setTimeout(()=> {}, 0)`会在什么时候执行。这就需要我们了解同步任务、异步任务的执行顺序,即前面讲到的**事件循环机制**。
|
||||
|
||||
解释:先等同步任务执行完成后,再执行异步任务。
|
||||
|
||||
同理,我们再来看看下面这段伪代码:
|
||||
|
||||
```js
|
||||
setTimeout(() => {
|
||||
console.log('异步任务');
|
||||
}, 2000);
|
||||
|
||||
// 伪代码
|
||||
sleep(5000); //表示很耗时的同步任务
|
||||
```
|
||||
|
||||
上面的代码中,异步任务不是2秒之后执行,而是等耗时的同步任务执行完毕之后,才执行。那这个异步任务,是在5秒后执行?还是在7秒后执行?这个作业,留给读者你来思考~
|
||||
|
||||
|
||||
### 举例 3(较真系列)
|
||||
|
||||
```js
|
||||
setTimeout(() => {
|
||||
console.log('异步任务');
|
||||
}, 1000);
|
||||
```
|
||||
|
||||
上面的代码中,等到 1 秒之后,真的会执行异步任务吗?其实不是。
|
||||
|
||||
在浏览器中, setTimeout()/ setInterval() 的每调用一次定时器的最小时间间隔是**4ms**,这通常是由于函数嵌套导致(嵌套层级达到一定深度),或者是由于已经执行的setInterval的回调函数阻塞导致的。
|
||||
|
||||
上面的案例中,异步任务需要等待1004毫秒之后,才会从 Event Table 进入到 Event Queue。这在面试中也经常被问到。
|
||||
|
||||
## 参考链接
|
||||
|
||||
- [JS-同步任务,异步任务,微任务,和宏任务](https://github.com/PleaseStartYourPerformance/javaScript/issues/34)
|
||||
- [JS 同步异步宏任务微任务](https://juejin.cn/post/6875605533127081992)、[JavaScript 中事件循环的理解](https://zhuanlan.zhihu.com/p/364475433)、[javascript 事件循环机制](https://github.com/reng99/blogs/issues/34)
|
||||
- [如何实现比 setTimeout 快 80 倍的定时器?](https://mp.weixin.qq.com/s/NqzWkeOhqAU85XPkJu_wCA)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||
## 同步和异步
|
||||
## 同步和异步回顾
|
||||
|
||||
### 同步和异步的简单理解
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
试想一下,如果没有异步刷新的话,每次点击“加载更多”,网页都要重新刷新,体验就太糟糕了。
|
||||
|
||||
web前端里的异步更新,就要用到 Ajax。
|
||||
web前端里的异步更新,就要用到 Ajax。很多人说,如果没有 Ajax,就没有互联网的今天。
|
||||
|
||||
关于同步和异步的更详细介绍,可以参考本项目的另外一篇文章:《JavaScript基础:ES6语法/单线程和异步》
|
||||
关于同步和异步的更详细介绍,可以参考本项目的另外一篇文章:《05-JavaScript基础:异步编程和Ajax/01-单线程和异步》
|
||||
|
||||
|
||||
|
||||
@@ -33,12 +33,11 @@ web前端里的异步更新,就要用到 Ajax。
|
||||
|
||||
### Ajax 的概念
|
||||
|
||||
在浏览器中,我们可以在不刷新页面的情况下,通过ajax的方式去获取一些新的内容。
|
||||
在浏览器中,我们可以在不刷新页面的情况下,通过 Ajax 的方式去获取一些新的内容。
|
||||
|
||||
Ajax:Asynchronous Javascript And XML(异步 JavaScript 和 XML)。它并不是凭空出现的新技术,而是对于现有技术的结合。Ajax 的核心是 js 对象:**XMLHttpRequest**。
|
||||
|
||||
|
||||
### 发送 Ajax 请求的五个步骤
|
||||
### Ajax原理(发送 Ajax 请求的五个步骤)
|
||||
|
||||
> 其实也就是 使用 XMLHttpRequest 对象的五个步骤。
|
||||
|
||||
@@ -54,11 +53,11 @@ Ajax:Asynchronous Javascript And XML(异步 JavaScript 和 XML)。它并
|
||||
|
||||
(1)创建异步对象,即 XMLHttpRequest 对象。
|
||||
|
||||
(2)使用open方法设置请求的参数。`open(method, url, async)`。参数解释:请求的方法、请求的url、是否异步。
|
||||
(2)使用open方法设置请求参数。`open(method, url, async)`。参数解释:请求的方法、请求的url、是否异步。
|
||||
|
||||
(3)发送请求。
|
||||
(3)发送请求:`send()`。
|
||||
|
||||
(4)注册事件。 注册onreadystatechange事件,状态改变时就会调用。
|
||||
(4)注册事件:注册onreadystatechange事件,状态改变时就会调用。
|
||||
|
||||
如果要在数据完整请求回来的时候才调用,我们需要手动写一些判断的逻辑。
|
||||
|
||||
@@ -84,29 +83,29 @@ Ajax:Asynchronous Javascript And XML(异步 JavaScript 和 XML)。它并
|
||||
document.querySelector('#btnAjax').onclick = function () {
|
||||
// 发送ajax 请求 需要 五步
|
||||
|
||||
// (1)创建异步对象
|
||||
var ajaxObj = new XMLHttpRequest();
|
||||
// (1)创建XMLHttpRequest对象
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
|
||||
// (2)设置请求的参数。包括:请求的方法、请求的url。
|
||||
ajaxObj.open('get', '02-ajax.php');
|
||||
xmlhttp.open('get', '02-ajax.php');
|
||||
|
||||
// (3)发送请求
|
||||
ajaxObj.send();
|
||||
xmlhttp.send();
|
||||
|
||||
//(4)注册事件。 onreadystatechange事件,状态改变时就会调用。
|
||||
//如果要在数据完整请求回来的时候才调用,我们需要手动写一些判断的逻辑。
|
||||
ajaxObj.onreadystatechange = function () {
|
||||
xmlhttp.onreadystatechange = function () {
|
||||
// 为了保证 数据 完整返回,我们一般会判断 两个值
|
||||
if (ajaxObj.readyState == 4 && ajaxObj.status == 200) {
|
||||
// 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
|
||||
// 5.在注册的事件中 获取 返回的 内容 并修改页面的显示
|
||||
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
|
||||
// 如果能够进入这个判断,说明数据请求成功了
|
||||
// (5)在注册的事件中,获取返回的内容,并显示在页面上
|
||||
console.log('数据返回成功');
|
||||
|
||||
|
||||
// 数据是保存在 异步对象的 属性中
|
||||
console.log(ajaxObj.responseText);
|
||||
console.log(xmlhttp.responseText);
|
||||
|
||||
// 修改页面的显示
|
||||
document.querySelector('h1').innerHTML = ajaxObj.responseText;
|
||||
// 显示在页面上
|
||||
document.querySelector('h1').innerHTML = xmlhttp.responseText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user