CodeAshen's blog CodeAshen's blog
首页
  • Spring Framework

    • 《剖析Spring5核心原理》
    • 《Spring源码轻松学》
  • Spring Boot

    • Spring Boot 2.0深度实践
  • Spring Cloud

    • Spring Cloud
    • Spring Cloud Alibaba
  • RabbitMQ
  • RocketMQ
  • Kafka
  • MySQL8.0详解
  • Redis从入门到高可用
  • Elastic Stack
  • 操作系统
  • 计算机网络
  • 数据结构与算法
  • 云原生
  • Devops
  • 前端
  • 实用工具
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • Reference
GitHub (opens new window)

CodeAshen

后端界的小学生
首页
  • Spring Framework

    • 《剖析Spring5核心原理》
    • 《Spring源码轻松学》
  • Spring Boot

    • Spring Boot 2.0深度实践
  • Spring Cloud

    • Spring Cloud
    • Spring Cloud Alibaba
  • RabbitMQ
  • RocketMQ
  • Kafka
  • MySQL8.0详解
  • Redis从入门到高可用
  • Elastic Stack
  • 操作系统
  • 计算机网络
  • 数据结构与算法
  • 云原生
  • Devops
  • 前端
  • 实用工具
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • Reference
GitHub (opens new window)
  • CSS3浮动定位与背景样式

  • CSS动画

  • JS基础语法与表达式

  • 流程控制语句与数组

  • JS函数与DOM

  • 面向对象

  • 正则表达式

  • ES6基础入门

  • ES6语法扩展

  • Promise与Class

    • 01-Promise
      • 什么是 Promise
      • Promise 的基本用法
        • 实例化 Promise
        • Promise 状态
        • then() 方法
        • resolve 和 reject 函数的参数
      • then()
        • then 方法回调的执行
        • then 方法的返回值
        • 回调地狱改造
      • catch()
      • finally()
    • 02-Class类
  • Module与Babel

  • 前端
  • Promise与Class
CodeAshen
2023-02-10
目录

01-Promise

# 初识 Promise

# 什么是 Promise

**Promise 是异步操作的一种解决方案。**之前我们就接触过异步的另一种方案:回调函数。

document.addEventListener(
  'click',
  () => {
    console.log('这里是异步的');
  },
  false
);
console.log('这里是同步的');
1
2
3
4
5
6
7
8

Promise 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题。

下例中演示了一个回调地狱,这种情况如果需要修改逻辑将非常头疼。

/**
 * 移动页面元素的函数
 * 
 * @param el 要转换的元素
 * @param {x=0, y=0} 移动的平面距离
 * @param end 移动完成后的回调
 */
const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
  el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
  el.addEventListener(
    'transitionend',
    () => {
      // console.log('end');
      end();
    },
    false
  );
};

// 获取要移动的元素
const boxEl = document.getElementById('box');

// 通过不断增加回调,使得元素移动四次(回调地狱)
document.addEventListener(
  'click',
  () => {
    move(boxEl, { x: 150 }, () => {
      move(boxEl, { x: 150, y: 150 }, () => {
        move(boxEl, { y: 150 }, () => {
          // console.log('object');
          move(boxEl, { x: 0, y: 0 });
        });
      });
    });
  },
  false
);
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
29
30
31
32
33
34
35
36
37

# Promise 的基本用法

# 实例化 Promise

使用 Promise,先要实例化构造函数生成实例对象,我们用的就是这个实例对象。

// 传入一个回调函数
const p = new Promise(() => {});
1
2

# Promise 状态

Promise 有 3 种状态:

  • 一开始是 pending(未完成)
  • 执行 resolve,变成 fulfilled(resolved),已成功
  • 执行 reject,变成 rejected,已失败
const p = new Promise((resolve, reject) => {
  // pending->fulfilled
  resolve();

  // pending->rejected
  // reject();
});
1
2
3
4
5
6
7

Promise 的状态一旦变化,就不会再改变了。这要如果同时执行 resolve 和 reject,会遵循第一次执行产生的状态。

image-20220822001433132

# then() 方法

Promise 的 then() 方法传入两个回调函数,分别是执行成功和执行失败的回调。

Const p = new Promise((resolve, reject) => resolve());

p.then(
    () => console.log('success callback'),
    () => console.error('error callback')
);
1
2
3
4
5
6

image-20220822001144048

# resolve 和 reject 函数的参数

new Promise((resolve, reject) => {
  // resolve({ username: 'alex' });
  reject(new Error('reason'));
}).then(
  data => console.log('success', data),
  err => console.log('error', err)
);
1
2
3
4
5
6
7

resolve 的参数,会传到 then 方法的成功回调函数参数上。

image-20220822002448644

reject 函数的参数,回传到 then 方法的失败回调函数参数上。

image-20220822002509374

# 实例方法

# then()

# then 方法回调的执行

  • pending -> fulfilled 时,执行 then 的第一个回调函数
  • pending -> rejected 时,执行 then 的第二个回调函数

# then 方法的返回值

then 方法执行后返回一个新的 Promise 对象。所以 then 方法可以链式使用。

new Promise((resolve, reject) => resolve())
.then(() => {}, () => {})
.then();
1
2
3

那么这个新的 Promise 对象是什么状态呢?

const p1 = new Promise((resolve, reject) => resolve());
    
const p2 = p1.then(
  data => console.log('success1', data),
  err => console.log('err1', err)
);

const p3 = p2.then(
  data => console.log('success2', data),
  err => console.log('err2', err)
);
1
2
3
4
5
6
7
8
9
10
11

默认 then 方法返回的 Promise 对象都是成功状态(fulfilled)的。

image-20220822004404081

其实产生的新 Promise 对象就是 then 方法的回调函数返回的。等价于 then 的两个回调都会在最后隐式返回一个成功状态的 Promise 对象。

const p1 = new Promise((resolve, reject) => resolve());

const p2 = p1.then(
  data => {
    console.log('success1', data);
    return new Promise((resolve, reject) => resolve(undefined));
  },
  err => {
    console.log('err1', err);
    return new Promise((resolve, reject) => resolve(undefined));
  }
);
1
2
3
4
5
6
7
8
9
10
11
12

即如果 then 回调函数不手动写返回值,会自动返回一个成功状态的 Promise 对象。

如果在 then 回调函数中返回一个非 Promise 对象的数据类型,会将其包装到 Promise 对象回调的 resolve 方法参数处。

const p1 = new Promise((resolve, reject) => resolve());

const p2 = p1.then(
  data => {
    console.log('success1', data);
    return 1;
  },
  err => {
    console.log('err1', err);
    return 2;
  }
);

// 等同于
const p2 = p1.then(
  data => {
    console.log('success1', data);
    return new Promise((resolve, reject) => resolve(1));
  },
  err => {
    console.log('err1', err);
    return new Promise((resolve, reject) => resolve(2));
  }
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

如果想要在 then 方法后得到一个失败状态的 Promise 对象怎么做呢?那就只能手动构造一个失败状态的 Promise 对象并返回了。

const p1 = new Promise((resolve, reject) => resolve());

const p2 = p1.then(
  data => {
    console.log('success1', data);
    // 会自动封装成 Promise 对象返回
    return 1;
  },
  err => {
    console.log('err1', err);
    // 手动返回失败状态的 Promise 对象
    return new Promise((resolve, reject) => reject('reason'));
  }
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 回调地狱改造

改造前面写的回调地狱。

/**
 * 移动页面元素的函数
 * 
 * @param el 要转换的元素
 * @param {x=0, y=0} 移动的平面距离
 * @param end 移动完成后的回调
 */
const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
  el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
  el.addEventListener(
    'transitionend',
    () => {
      // console.log('end');
      end();
    },
    false
  );
};

const movePromise = (el, point) => {
  return new Promise(resolve => {
    move(el, point, () => {
      resolve();
    });
  });
};

// 获取要移动的元素
const boxEl = document.getElementById('box');

document.addEventListener(
  'click',
  () => {
    movePromise(boxEl, { x: 150 })
      .then(() => {
        return movePromise(boxEl, { x: 0, y: 0 });
      })
      .then(() => {
        return movePromise(boxEl, { x: 150, y: 150 });
      })
      .then(() => {
        return movePromise(boxEl, { y: 150 });
      });
  },
  false
);
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# catch()

实际使用 then 方法时一般只传入一个参数,即成功状态的回调函数。而失败状态时交给 catch 方法处理。

catch 专门用来处理 rejected 状态,catch 本质上是 then 的特例,相当于 then(null, err => {})。

new Promise((resolve, reject) => {
  // resolve(123);
  reject('reason');
})
.then(data => console.log(data))
.catch(err => console.log(err));
1
2
3
4
5
6

catch() 可以捕获它前面的错误,一般总是建议,Promise 对象后面要跟 catch 方法,这样可以处理 Promise 内部发生的错误。

# finally()

当 Promise 状态发生变化时,不论如何变化都会执行 finally() 方法,不变化不执行。

new Promise((resolve, reject) => {
  // resolve(123);
  reject('reason');
})
.finally(data => console.log(data))
.catch(err => {});
1
2
3
4
5
6

finally() 本质上也是 then() 的特例,等同于:

new Promise((resolve, reject) => {
  // resolve(123);
  reject('reason');
})
.then(
  result => {
    return result;
  },
  err => {
    return new Promise((resolve, reject) => {
      reject(err);
    });
  }
)
.then(data => {
  console.log(data);
})
.catch(err => {
  console.log(err);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 构造函数方法

# 注意事项和应用

编辑 (opens new window)
上次更新: 2023/06/04, 12:34:19
04-ES6新增方法
02-Class类

← 04-ES6新增方法 02-Class类→

最近更新
01
第01章-RabbitMQ导学
02-10
02
第02章-入门RabbitMQ核心概念
02-10
03
第03章-RabbitMQ高级特性
02-10
更多文章>
Theme by Vdoing | Copyright © 2020-2023 CodeAshen | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式