Web/10-ES6/07-ES6:Promise详解.md
2020-04-07 20:42:03 +08:00

385 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 异步调用
### 异步
JavaScript的执行环境是**单线程**。
所谓单线程是指JS引擎中负责解释和执行JavaScript代码的线程只有一个也就是一次只能完成一项任务这个任务执行完后才能执行下一个它会「阻塞」其他任务。这个任务可称为主线程
异步模式可以一起执行**多个任务**。
### 接口调用的方式
js 中常见的接口调用方式,有以下几种:
- 原生ajax
- 基于jQuery的ajax
- Fetch
- Promise
- axios
### 多次异步调用的依赖分析
- 多次异步调用的结果,顺序不同步。
- 异步调用的结果如果**存在依赖**,则需要嵌套。
在ES5中当进行多层嵌套回调时会导致代码层次过多很难进行维护和二次开发而且会导致**回调地狱**的问题。而ES6中的Promise 就可以解决这两个问题。
## Promise 概述
### Promise的介绍和优点
ES6中的Promise 是异步编程的一种方案。从语法上讲Promise 是一个对象,它可以获取异步操作的消息。
Promise对象, 可以**将异步操作以同步的流程表达出来**。使用 Promise 主要有以下好处:
- 可以很好地解决**回调地狱**的问题(避免了层层嵌套的回调函数)。
- 语法更佳简洁。Promise 对象提供了简洁的API使得控制异步操作更加容易。
### 回调地狱的举例
假设买菜、做饭、洗碗都是异步的。
现在的流程是:买菜成功之后,才能开始做饭。做饭成功后,才能开始洗碗。这里面就涉及到了回调的嵌套。
ES6的Promise是一个构造函数, 用来生成promise实例。
### promise对象的3个状态
- 初始化状态等待状态pending
- 成功状态fullfilled
- 失败状态rejected
### 使用promise的基本步骤
1创建promise对象
2调用promise的**回调函数**then()
代码格式如下:
```javascript
let promise = new Promise((resolve, reject) => {
//进来之后状态为pending
console.log('111'); //这一行代码是同步的
//开始执行异步操作这里开始写异步的代码比如ajax请求 or 开启定时器)
if (异步的ajax请求成功) {
console.log('333');
resolve();//如果请求成功了请写resolve()此时promise的状态会被自动修改为fullfilled
} else {
reject();//如果请求失败了请写reject()此时promise的状态会被自动修改为rejected
}
})
console.log('222');
//调用promise的then()
promise.then(() => {
//如果promise的状态为fullfilled则执行这里的代码
console.log('成功了');
}
, () => {
//如果promise的状态为rejected则执行这里的代码
console.log('失败了');
}
)
```
代码解释:
1当new Promise()执行之后promise对象的状态会被初始化为`pending`,这个状态是初始化状态。`new Promise()`这行代码括号里的内容是同步执行的。括号里定义一个functionfunction有两个参数resolve和reject。如下
- 如果请求成功了请写resolve()此时promise的状态会被自动修改为fullfilled。
- 如果请求失败了请写reject()此时promise的状态会被自动修改为rejected
2promise.then()方法,括号里面有两个参数,分别代表两个函数 function1 和 function2
- 如果promise的状态为fullfilled意思是如果请求成功则执行function1里的内容
- 如果promise的状态为rejected意思是如果请求失败则执行function2里的内容
另外resolve()和reject()这两个方法是可以给promise.then()传递参数的。如下:
```javascript
let promise = new Promise((resolve, reject) => {
//进来之后状态为pending
console.log('111'); //这行代码是同步的
//开始执行异步操作这里开始写异步的代码比如ajax请求 or 开启定时器)
if (异步的ajax请求成功) {
console.log('333');
resolve('haha');//如果请求成功了请写resolve()此时promise的状态会被自动修改为fullfilled
} else {
reject('555');//如果请求失败了请写reject()此时promise的状态会被自动修改为rejected
}
})
console.log('222');
//调用promise的then()
promise.then((successMsg) => {
//如果promise的状态为fullfilled则执行这里的代码
console.log(successMsg, '成功了');
}
, (errorMsg) => {
//如果promise的状态为rejected则执行这里的代码
console.log(errorMsg, '失败了');
}
)
```
### ajax请求的举例涉及到嵌套的回调
```javascript
//定义一个请求news的方法
function getNews(url) {
//创建一个promise对象
let promise = new Promise((resolve, reject) => {
//初始化promise状态为pending
//启动异步任务
let request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
let news = request.response;
resolve(news);
} else {
reject('请求失败了。。。');
}
}
};
request.responseType = 'json';//设置返回的数据类型
request.open("GET", url);//规定请求的方法,创建链接
request.send();//发送
})
return promise;
}
getNews('http://localhost:3000/news?id=2')
.then((news) => {
console.log(news);
document.write(JSON.stringify(news));
console.log('http://localhost:3000' + news.commentsUrl);
return getNews('http://localhost:3000' + news.commentsUrl);
}, (error) => {
alert(error);
})
.then((comments) => {
console.log(comments);
document.write('<br><br><br><br><br>' + JSON.stringify(comments));
}, (error) => {
alert(error);
})
```
### 参考链接
- [当面试官问你Promise的时候他究竟想听到什么](https://zhuanlan.zhihu.com/p/29235579)
## Symbol
### 概述
背景ES5中对象的属性名都是字符串容易造成重名污染环境。
**概念**ES6 引入了一种新的原始数据类型Symbol表示独一无二的值。它是 JavaScript 语言的第七种数据类型前六种是undefined、null、布尔值Boolean、字符串String、数值Number、对象Object
**特点:**
- Symbol属性对应的值是唯一的解决**命名冲突问题**
- Symbol值不能与其他数据进行计算包括同字符串拼串
- for in、for of 遍历时不会遍历Symbol属性。
### 创建Symbol属性值
Symbol是函数但并不是构造函数。创建一个Symbol数据类型
```javascript
let mySymbol = Symbol();
console.log(typeof mySymbol); //打印结果symbol
console.log(mySymbol); //打印结果Symbol()
```
打印结果:
![](http://img.smyhvae.com/20180317_1134.png)
下面来讲一下Symbol的使用。
### 1、将Symbol作为对象的属性值
```javascript
let mySymbol = Symbol();
let obj = {
name: 'smyhvae',
age: 26
};
//obj.mySymbol = 'male'; //错误:不能用 . 这个符号给对象添加 Symbol 属性。
obj[mySymbol] = 'hello'; //正确:通过**属性选择器**给对象添加 Symbol 属性。后面的属性值随便写。
console.log(obj);
```
上面的代码中我们尝试给obj添加一个Symbol类型的属性值但是添加的时候不能采用`.`这个符号,而是应该用`属性选择器`的方式。打印结果:
![](http://img.smyhvae.com/20180317_1134.png)
现在我们用for in尝试对上面的obj进行遍历
```javascript
let mySymbol = Symbol();
let obj = {
name: 'smyhvae',
age: 26
};
obj[mySymbol] = 'hello';
console.log(obj);
//遍历obj
for (let i in obj) {
console.log(i);
}
```
打印结果:
![](http://img.smyhvae.com/20180317_1134.png)
从打印结果中可以看到for in、for of 遍历时不会遍历Symbol属性。
### 创建Symbol属性值时传参作为标识
如果我通过 Symbol()函数创建了两个值,这两个值是不一样的:
```javascript
let mySymbol1 = Symbol();
let mySymbol2 = Symbol();
console.log(mySymbol1 == mySymbol2); //打印结果false
console.log(mySymbol1); //打印结果Symbol()
console.log(mySymbol2); //打印结果Symbol()
```
![](http://img.smyhvae.com/20180317_1134.png)
上面代码中,倒数第三行的打印结果也就表明了,二者的值确实是不相等的。
最后两行的打印结果却发现,二者的打印输出,肉眼看到的却相同。那该怎么区分它们呢?
既然Symbol()是函数,函数就可以传入参数,我们可以通过参数的不同来作为**标识**。比如:
```javascript
//在括号里加入参数来标识不同的Symbol
let mySymbol1 = Symbol('one');
let mySymbol2 = Symbol('two');
console.log(mySymbol1 == mySymbol2); //打印结果false
console.log(mySymbol1); //打印结果Symbol(one)
console.log(mySymbol2); //打印结果Symbol(two)。颜色为红色。
console.log(mySymbol2.toString());//打印结果Symbol(two)。颜色为黑色。
```
打印结果:
![](http://img.smyhvae.com/20180317_1134.png)
### 定义常量
Symbol 可以用来定义常量:
```javascript
const MY_NAME = Symbol('my_name');
```
### 内置的 Symbol 值
除了定义自己使用的 Symbol 值以外ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
- `Symbol.iterator`属性
对象的`Symbol.iterator`属性,指向该对象的默认遍历器方法。
## async函数异步函数
### 概述
> async 函数是在 ES2017 引入的。
概念:真正意义上去解决异步回调的问题,同步流程表达异步操作。
本质: Generator 的语法糖。
async比之前的 Promise、Generator要好用一些。
语法:
```javascript
async function foo() {
await 异步操作;
await 异步操作
}
```
我们在普通的函数前面加上 async 关键字,就成了 async 函数。
### async、Promise、Generator的对比async的特点
1、不需要像Generator去调用next方法遇到await等待当前的异步操作完成就往下执行。
2、async返回的总是Promise对象可以用then方法进行下一步操作。
3、async取代Generator函数的星号*await取代Generator的yield。
4、语意上更为明确使用简单经临床验证暂时没有任何副作用。