Promise 教程

为什么需要 Promise

假如现在有一个需求, 通过 Ajax 请求一个 ID, 再根据这个 ID 请求用户名. 下面是直接使用 ajax 写的一个示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="./jquery.js"></script>
</head>
<body>
<script>
// 发送ajax请求
$.ajax({
type: "GET",
url: "./data1.json",
success: (res) => {
// 这里的res是返回结果
console.log(res);
},
});
</script>
</body>
</html>

我们存在这样的文件目录:

|240

拿到 ID 了, 接下来要再次请求用户名, 这个 ajax 请求在哪里进行发送呢? 肯定不能是在下面直接进行请求, 因为我们需要参数. 那么代码就会变成这样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<script src="./jquery.js"></script>
</head>
<body>
<script>
// 发送ajax请求
$.ajax({
type: "GET",
url: "./data1.json",
success: (res) => {
// 对象的结构赋值
const { id } = res;

// 成功了以后 再次拿id发送请求
$.ajax({
type: "GET",
data: {id},
url: "/data2.json",
success: (res) => {},
});
},
});
</script>
</body>
</html>

可以看到, 代码的层次嵌套的越来越多, 也就越来越麻烦了. 为了解决这个问题, 我们出现了 Promise 这个东西.

[!tip] Promise
主要是解决了 回调地狱 的问题.

Promise 的基本使用

Promise 其实就是一个构造函数, 那么自然就可以通过 new 实例化一个对象出来.

在 Promise 的构造函数中, 参数为一个函数. 这个函数接收两个参数: resolvereject.

1
2
3
// 调用构造函数 实例化对象
const p = new Promise((resolve, reject) => {});
console.log(p);

控制台输出为:

|330

这里可以看到这个对象有两个重要的属性: State 状态Resule 结果.

Promise 状态

Promise 有三种状态:

  1. pending, 可以理解为等待, 进行中, 待解决
  2. fullfilled, 可以理解为已完成, 成功
  3. rejected, 拒绝, 也就是失败

Promise 状态的改变

可以通过调用 resolve 和 reject 改变当前 promise 对象的状态.

1
2
3
4
5
6
7
8
// 这里的resolve和reject其实都是函数
// 函数自然可以调用
const p = new Promise((resolve, reject) => {
// 调用函数可以改状态为fullfilled
resolve();
});
// 再次输出
console.dir(p);

现在 promise 的状态已经改变了.

|330

另外一个函数自然就是 rejected 了, 这里不做额外演示.


这里需要注意, 状态的改变是一次性的, 只要改变过一遍了, 就不能继续进行改变了.

Promise 结果

resolve 是一个函数, 那么函数自然是有参数的. 不妨调用一下 resolve 并且传递参数:

1
2
3
4
const p = new Promise((resolve, reject) => {
resolve("成功的结果");
});
console.dir(p);

|330

可以看到, 通过调用 resolve 传递参数, 改变了 当前 promise 对象 的结果.


自然, reject 也是可以的.

1
2
3
4
const p = new Promise((resolve, reject) => {
reject("失败的结果");
});
console.dir(p);

|330

可以看到, 错误的结果不仅在当前 promise 对象中显示出来了, 也在报错中显示出来了.

Promise 的方法

then

介绍

then 是 promise 原型上的最常用的方法, 也可以说是 Promise 上最重要的一个方法. then 接收两个参数, 第一个是一个函数, 第二个也是一个函数, 不过对应的内容不一样. 代码如下:

1
2
3
4
5
6
7
8
9
const p = new Promise((resolve, reject) => {
resolve("成功的结果");
});

// 这里的p是一个实例 那么就可以使用原型上的方法
// 第一个参数是成功地时候调用的函数
p.then(() => {
console.log("成功时调用的函数");
});

这里本来就是成功地, 所以会自动调用成功时的函数. 第二个参数就是失败的情况了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const p = new Promise((resolve, reject) => {
resolve("成功的结果");
});

// 这里的p是一个实例 那么就可以使用原型上的方法
// 第一个参数是成功地时候调用的函数
p.then(
() => {
console.log("成功时调用的函数");
},
() => {
console.log("失败时调用的函数");
}
);

当 promise 的状态是 fullfilled 的时候, 就是成功; 当 Promise 的状态为 rejected 时就是失败. 只要改成调用 reject() 就会显示失败.

获取数据

我们肯定不希望只直到成功或者失败, 更需要知道的是 promise 的结果是什么. 作为函数, 函数自然是有参数的. 参数其实就是 value, 也就是上面 resolve 或者 reject 里面的值.

1
2
3
4
5
6
7
8
9
const p = new Promise((resolve, reject) => {
resolve("成功啦");
});

// 这里的p是一个实例 那么就可以使用原型上的方法
// 第一个参数是成功地时候调用的函数
p.then((res) => {
console.log("成功时调用的函数", res);
});

那么输出为:

1
成功时调用的函数 成功啦

出错, 我们的形参就代表的是出错的原因了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const p = new Promise((resolve, reject) => {
// resolve("成功啦");
reject("请求失败");
});

// 这里的p是一个实例 那么就可以使用原型上的方法
// 第一个参数是成功地时候调用的函数
p.then(
(res) => {
console.log("成功啦", res);
},
(err) => {
console.log("出错啦, 原因为:", err);
}
);

输出为:

1
出错啦, 原因为: 请求失败

至此, 我们就拿到了所有我们需要的数据了.

[!success] 通常来说
我们使用 res 作为成功的形参, 使用 err 作为错误的形参

返回值

then 方法其实也是有返回值的, then 方法的返回值依然时一个 Promise 对象, 类型时 pending. 这就让 Promise 支持链式操作了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const p = new Promise((resolve, reject) => {
// 模拟成功
resolve({
name: "yyt",
age: 18,
});
});

// 这里的p是一个实例 那么就可以使用原型上的方法
// 第一个参数是成功地时候调用的函数
p.then(
(res) => {
console.log("成功啦", res);
},
(err) => {
console.log("出错啦, 原因为:", err);
}
)
.then((res) => {
console.log("成功2");
})
.then((res) => {
console.log("成功3");
});

自然, 代码会按照顺序输出需要的值.

|330

修改状态

then 方法中可以通过 return 来修改 Promise 的状态. 只要 return 了内容, 那么原本 Promise 的状态会自动变成 fullfilled.

如果代码出错, 则状态会变为 rejected.

catch

catch 和 then 差不多, 也是传入一个回调函数. 当 Promise 的状态改为 rejected 的时候, 或者代码执行出错的时候, 会执行 catch 方法, 并且捕获错误.

1
2
3
4
5
6
7
8
9
const p = new Promise((resolve, reject) => {
// 假设代码存在问题
console.log(abc);
});

// 调用
p.catch((err) => {
console.log("错误了", err);
});

|330

reject 也是会报错的.

1
2
3
4
5
6
7
8
const p = new Promise((resolve, reject) => {
reject("这是一个错误");
});

// 调用
p.catch((err) => {
console.log("错误了", err);
});

输出如下:

1
错误了 这是一个错误

一般来说, then 的第二个参数也是报错, 但是我们更喜欢使用 catch 的方式, 这样代码是扁平化, 最好看的一种.

总结

至此, Promise 的教程完结啦, 很简单但是很重要的一个部分, 希望对你有所帮助!

如果需要查看文档, 可以前往 Promise - JavaScript | MDN 进行查看.