一个新事物的产生一定是为了解决什么问题或者优化什么东西。我们要深层理解promise与async await 就需要回到问题最早出现的地方。
一、异步
1、为什么会产生异步?
我们都知道js是单线程的,浏览器是多进程的,但为什么这么设计呢?
首先我们先来了解一下什么是进程、线程:
进程是cpu分配资源的最小单位。线程是cpu调度的最小单位。线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程。
一个系统中,会存在很多进程,它们都会占用独立的内存。每个进程对应许多线程,它会告诉中央处理器(CPU)如何运行代码。
正因为js代码是顺序执行的,很多时候,比如网络请求数据返回时间是30s,这个时间不可能一直等待,就先跳过这个环节,向下执行,直到数据返回在执行对应代码,这就是异步。
同步会按照代码顺序执行,而异步不按照代码顺序执行,所以它执行效率更高。
2、什么时候会用到异步?
- 定时任务:setTimeout、setInterval
- 网络请求:ajax 请求、ajax图片加载
- 事件监听器:addEventListener
二、异步编程
我们针对异步操作选择了哪些方法==>
1、回调函数
其实就是函数里面调用函数,把B函数放在A函数的回调里面。
缺点:
- 过多的回调容易形成回调地狱(最大缺点)
- 不利于代码的阅读和维护
- 每个任务只能指定一个回调函数
- 不能捕获异常
2、promise
Promise 是异步编程的一种解决方案,其实是一个构造函数,它的状态不受外界影响。
它不仅可以避免回调地狱,也能捕获异常。
存在三种状态:
pending | 初始状态 |
fulfilled | 意味着操作成功完成 |
rejected | 意味着操作失败 |
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。一旦状态改变,就不会再变,任何时候都可以得到这个结果。
优点 | 1、可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,链式操作减低了编码难度 2、提供统一的接口,使得控制异步操作更加容易 3、代码可读性明显增强 |
缺点 | 1、无法取消,一旦新建它就会立即执行,无法中途取消 2、如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部 3、当处于 Pending 状态时,无法得知目前进展到哪一个阶段 |
Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。
Promise构造函数存在以下方法:
Promise.all() | 可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值,其余不会被返回。 |
Promse.race() | Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。 |
Promse.allSettled() | 接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例,所有的方法执行完后调用 |
Promse.any() | 有一个.then()成功执行就会调用 |
Promise构建出来的实例存在以下方法:
then() | 是实例状态发生改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数 |
catch() | 是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数 |
finally() | 用于指定不管 Promise 对象最后状态如何,都会执行的操作 |
3、Generator 函数
Genertor 函数是es6 新增的一种异步编程的解决方案,语法和传统的函数完全不同;Genertor 函数的最大的特点就是可以交出函数的执行权(即暂停执行)。
特征:
1、function关键字与函数名之间有一个星号
2、函数体内部使用yield表达式(暂停执行的标记),定义不同的内部状态
运行逻辑:
1、遇到 yield 表达式,就暂停执行后面的操作,并将紧跟在 yield 后面的那个表达式的值,作为返回的对象的 value 属性值。
2、下一次调用 next 方法时,再继续往下执行,直到遇到下一个 yield 表达式。
3、如果没有再遇到新的 yield 表达式,就一直运行到函数结束,直到 return 语句为止,并将 return 语句后面的表达式的值,作为返回的对象的 value 属性值。
4、如果该函数没有 return 语句,则返回的对象的 value 属性值为 undefined 。
注意:Generator并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator接口...)
4、async/await
Generator函数的语法糖。更为简洁,语义化更强。
async 作为一个关键字放到函数的前面,用于表示函数是一个异步函数,该函数的执行不会阻塞后面代码的执行。await是等待,只能放到async函数里面,在后面放一个返回promise对象的表达式
优点:
简洁、流程清晰,直观、语义明显。使用async和await明显节约了不少代码,不需要.then,不需要写匿名函数处理promise的resolve的值,不需要定义多余的data变量,还避免了嵌套代码。 |
内置执行器,Generator 函数的执行必须依靠执行器,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样 |
async/await让try/catch 可以同时处理同步和异步错误。try/catch不能处理JSON.parse的错误,因为他在promise中。此时需要.catch,这样的错误处理代码非常冗余。并且,在我们的实际生产代码会更加复杂 |
返回值是 Promise,async 函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then() 方法进行调用 |
三、async/await与promise的区别
相同点:promise和 async/await都是解决异步编程的一种方式
区别:
1、promise编写代码相比Generator、async更为复杂化,且可读性也稍差
2、Generator、async需要与promise对象搭配处理异步情况
3、async实质是Generator的语法糖,相当于会自动执行Generator函数
4、async使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案