Skip to content

Commit

Permalink
add: 宏任务和微任务
Browse files Browse the repository at this point in the history
  • Loading branch information
qianguyihao committed May 27, 2021
1 parent 48a1ab2 commit 61d5643
Show file tree
Hide file tree
Showing 23 changed files with 185 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ setTimeout(() => {

上面的代码中,等到 1 秒之后,真的会执行异步任务吗?其实不是。

在浏览器中, setTimeout()/ setInterval() 的每调用一次定时器的最小时间间隔是**4ms**,这通常是由于函数嵌套导致(嵌套层级达到一定深度),或者是由于已经执行的 setInterval 的回调函数阻塞导致的。
在浏览器中, setTimeout()/ setInterval() 的每调用一次定时器的最小时间间隔是**4毫秒**,这通常是由于函数嵌套导致(嵌套层级达到一定深度),或者是由于已经执行的 setInterval 的回调函数阻塞导致的。

上面的案例中,异步任务需要等待 1004 毫秒之后,才会从 Event Table 进入到 Event Queue。这在面试中也经常被问到。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ xmlhttp.onreadystatechange = function () {
封装 Ajax 请求的代码如下:(get 请求为例)

```js
// 封装 Ajax为公共函数
function myAjax(url, callback) {
// 封装 Ajax为公共函数:传入回调函数 success 和 fail
function myAjax(url, success, fail) {
// 1、创建XMLHttpRequest对象
var xmlhttp;
if (window.XMLHttpRequest) {
Expand All @@ -210,7 +210,10 @@ function myAjax(url, callback) {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
var obj = JSON.parse(xmlhttp.responseText);
console.log('数据返回成功:' + Jobj);
callback(obj);
success && success(xmlhttp.responseText);
} else {
// 这里的 && 符号,意思是:如果传了 fail 参数,就调用后面的 fail();如果没传 fail 参数,就不调用后面的内容。因为 fail 参数不一定会传。
fail && fail(new Error('接口请求失败'));
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,17 @@ myPromise()

上面的伪代码可以看出,即便在业务逻辑上是层层嵌套,但是代码写法上,却十分优雅,也没有过多的嵌套。

## Promise 基础
## Promise 对象的用法和状态

### Promise 的基本用法
### 使用 Promise 的基本步骤

(1)通过 `new Promise()` 构造出一个 Promise 实例。Promise 的构造函数中传入一个参数,这个参数是一个函数,该函数用于处理异步任务。

(2)函数中传入两个参数:resolve reject,分别表示异步执行成功后的回调函数和异步执行失败后的回调函数。代表着我们需要改变当前实例的状态到**已完成**或是**已拒绝**

(3)通过 promise.then() 处理返回结果(这里的 `promise` 指的是 Promise 实例)。
(3)通过 promise.then() promise.catch() 处理返回结果(这里的 `promise` 指的是 Promise 实例)。

Promise 的精髓在于**对异步操作的状态管理**
看到这里,你估计还是不知道 Promise 怎么使用。我们不妨来看一下 Promise 有哪些状态,便一目了然。要知道,Promise 的精髓在于**对异步操作的状态管理**

### promise 对象的 3 个状态

Expand All @@ -163,57 +163,121 @@ Promise 的精髓在于**对异步操作的状态管理**。

- 失败:rejected

(1)当 new Promise()执行之后,promise 对象的状态会被初始化为`pending`,这个状态是初始化状态。`new Promise()`这行代码,括号里的内容是同步执行的。括号里定义一个 function,function 有两个参数:resolve reject。如下:
**步骤 1**

new Promise()执行之后,promise 对象的状态会被初始化为`pending`,这个状态是初始化状态。`new Promise()`这行代码,括号里的内容是同步执行的。括号里可以再定义一个 异步任务的 function,function 有两个参数:resolve reject。如下:

- 如果请求成功了,则执行 resolve(),此时,promise 的状态会被自动修改为 fulfilled。

- 如果请求失败了,则执行 reject(),此时,promise 的状态会被自动修改为 rejected

(2)promise.then()方法,括号里面有两个参数,分别代表两个函数 function1 function2:
(2)promise.then()方法:**只有 promise 的状态被改变之后,才会走到 then 或者 catch**。也就是说,在 new Promise()的时候,如果没有写 resolve(),则 promise.then() 不执行;如果没有写 reject(),则 promise.catch() 不执行。

`then()`括号里面有两个参数,分别代表两个函数 function1 function2:

- 如果 promise 的状态为 fulfilled(意思是:如果请求成功),则执行 function1 里的内容

- 如果 promise 的状态为 rejected(意思是,如果请求失败),则执行 function2 里的内容

另外,resolve() reject()这两个方法,是可以给 promise.then()传递参数的。

关于 promise 的状态改变,伪代码及注释如下:
关于 promise 的状态改变,以及如何处理状态改变,伪代码及注释如下:

```javascript
// 创建 promise 实例
let promise = new Promise((resolve, reject) => {
//进来之后,状态为pending
console.log('111'); //这行代码是同步的
console.log('同步代码'); //这行代码是同步的
//开始执行异步操作(这里开始,写异步的代码,比如ajax请求 or 开启定时器)
if (异步的ajax请求成功) {
console.log('333');
resolve('haha'); //如果请求成功了,请写resolve(),此时,promise的状态会被自动修改为fulfilled
resolve('请求成功,并传参'); //如果请求成功了,请写resolve(),此时,promise的状态会被自动修改为fulfilled(成功状态)
} else {
reject('555'); //如果请求失败了,请写reject(),此时,promise的状态会被自动修改为rejected
reject('请求失败,并传参'); //如果请求失败了,请写reject(),此时,promise的状态会被自动修改为rejected(失败状态)
}
});
console.log('222');
//调用promise的then()
//调用promise的then():开始处理成功和失败
promise.then(
(successMsg) => {
//如果promise的状态为fulfilled,则执行这里的代码
console.log(successMsg, '成功了');
// 处理 promise 的成功状态:如果promise的状态为fulfilled,则执行这里的代码
console.log(successMsg, '成功了'); // 这里的 successMsg 是前面的 resolve('请求成功,并传参') 传过来的参数
},
(errorMsg) => {
//如果promise的状态为rejected,则执行这里的代码
console.log(errorMsg, '失败了');
//处理 promise 的失败状态:如果promise的状态为rejected,则执行这里的代码
console.log(errorMsg, '失败了'); // 这里的 errorMsg 是前面的 reject('请求失败,并传参') 传过来的参数
}
);
```

**几点补充**
上面的注释要多看几遍。

## 几点补充

### new Promise() 是同步代码

`new Promise()`这行代码本身是同步的。promise 如果没有使用 resolve reject 更改状态时,状态为 pending。

**举例1**

```js
const promiseA = new Promise((resolve, reject) => {});
console.log(promiseA); // 此时 promise 的状态为 pending(准备阶段)
```

(1)Promise 的状态一旦改变,就不能再变
上面的代码中,我既没有写 reslove(),也没有写 reject()。也就是说,这个 promise 一直处于准备阶段

(2)Promise 的状态改变,是不可逆的
当完成异步任务之后,状态分为成功或失败,此时我们就可以用 reslove() reject() 来修改 promise 的状态

为了解释这两点,我们来看个例子:
**举例2**

```js
new Promise((resolve, reject) => {
console.log('promise1'); // 这行代码是同步代码,会立即执行
}).then((res) => {
console.log('promise then:' + res); // 这行代码不会执行,因为前面没有写 resolve(),所以走不到 .then
});
```

打印结果:

```
promise1
```


上方代码,仔细看注释:如果前面没有写 `resolve()`,那么后面的 `.then`是不会执行的。

**举例3**

```js
new Promise((resolve, reject) => {
resolove();
console.log('promise1'); // 代码1:同步任务,会立即执行
}).then(res => {
console.log('promise then)'; // 代码2:异步任务中的微任务
})
console.log('千古壹号'); // 代码3:同步任务
```

打印结果:

```
promise1
千古壹号
promise then
```

代码解释:代码1是同步代码,所以最先执行。代码2是**微任务**里面的代码,所以要先等同步任务(代码3)先执行完。当写完`resolve();`之后,就会立刻把 `.then()`里面的代码加入到微任务队列当中。

补充知识:异步任务分为“宏任务”、“微任务”两种。我们到后续的章节中再详细讲。


### Promise 的状态一旦改变,就不能再变

代码举例:

```js
const p = new Promise((resolve, reject) => {
Expand All @@ -229,6 +293,8 @@ p.then((res) => {

上方代码的打印结果是 1,而不是 2,详见注释。

### Promise 的状态改变,是不可逆的

### 小结

1、promise 有三种状态:等待中、成功、失败。等待中状态可以更改为成功或失败,已经更改过状态后⽆法继续更改(例如从失败改为成功)。
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
## 准备知识

- 在执行一个 Promise 对象的时候,当走完`resolve();`之后,就会立刻把 `.then()`里面的代码加入到**微任务队列**当中。

- 任务的一般执行顺序:**同步任务 --> 微任务 --> 宏任务**

## 代码举例

### 举例 1:宏任务和微任务的执行顺序

```js
setTimeout(() => {
// 宏任务
console.log('setTimeout');
}, 0);
new Promise((resolve, reject) => {
resolve();
console.log('promise1'); // 同步任务
}).then((res) => {
// 微任务
console.log('promise then');
});
console.log('qianguyihao'); // 同步任务
```

打印结果:

```
promise1
qianguyihao
promise then
setTimeout
```

上方代码执行的顺序依次是:**同步任务 --> 微任务 --> 宏任务**

### 举例 2:宏任务和微任务的嵌套

```js
new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
console.log('setTimeout');
}, 0);
console.log('promise1');
}).then((res) => {
// 微任务
console.log('promise then');
});
console.log('qianguyihao');
```

打印结果:

```
promise1
qianguyihao
setTimeout
promise then
```

上方代码解释:在执行宏任务的**过程中**,创建了一个微任务。但是需要**先把当前这个宏任务执行完**,再去轮询异步任务的队列,进而执行微任务。
28 changes: 28 additions & 0 deletions 07-JavaScript进阶/Promise的一些题目.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,31 @@ a成功
b成功
b接口返回的内容
```

### 题目 3

举例1:

```js
new Promise((resolve, reject) => {
resolove();
console.log('promise1'); // 代码1
}).then(res => {
console.log('promise then)'; // 代码2:微任务
})
console.log('千古壹号'); // 代码3
```

打印结果:

```
promise1
千古壹号
promise then
```

代码解释:代码1是同步代码,所以最先执行。代码2是**微任务**里面的代码,所以要先等同步任务(代码3)先执行完。

当写完`resolove();`之后,就会立刻把 `.then()`里面的代码加入到微任务队列当中。

0 comments on commit 61d5643

Please sign in to comment.