Javascript异步编程
异步编程
概念
同步:一定要等任务执行完了,得到结果,才执行下一个任务。(类似于串行)
1 | A -> B -> AJAX 请求 -> C --------------------------- |
异步:不等任务执行完,直接执行下一个任务。(类似于并行)
1 | A -> B -> C --------------------------------------- |
当各个任务相互独立的时候,可以使用异步的方式执行,可以加速运行效率
1 | console.log("A") |
回调函数
回调函数可以告诉异步任务下一步做什么。
当A B两个函数存在运行顺序的时候,可以使用嵌套回调函数进行维护。比如我们希望先打印A,过了一秒钟之后再打印B
1 | function A() { |
但是当需要顺序运行的函数非常多时,也会导致嵌套过多,导致代码横向的增长,不利于代码理解与维护,这也是JS中常提到的“回调地狱”。
同时,嵌套函数存在耦合性,修改起来很麻烦
Promise
构造Promise对象:
1 | var a = new Promise((resolve, reject) => { |
resolve 和 reject 都是函数,调用 resolve 代表一切正常,调用reject 代表出现异常
****三种状态
- Pending 待定:最初始状态
- fulfilled 解决:执行了resolve()函数后,状态变为fulfilled,并执行then里的方法
- rejected 拒绝:执行了reject()函数后,状态变为rejected,并执行catch里的方法
1 | var a = new Promise((resolve, reject) => { |
在相应的函数中执行完了之后,Promise的状态还会改变
**then(): **
- 正常返回的时候,Promise的状态是fulfilled
- 报错的时候,Promise的状态是rejected
1 | var a = new Promise((resolve, reject) => { |
catch():
- 正常返回的时候,Promise状态是fulfilled,可以继续往下执行then函数
- 报错的时候,Promise状态是rejected
由于then块默认会向下顺序执行,return无法将其中断,只能通过throw来跳转至catch实现中断。
总结
- Promise 对象是异步编程的一种解决方案,一个 Promise 实例有三种状态,分别是pending、resolved 和 rejected,分别代表了进行中、已成功和已失败。
- 实例的状态只能由 pending 转变 resolved 或者rejected 状态,并且状态一经改变,就凝固了,无法再被改变了。
- 状态的改变是通过 resolve() 和 reject() 函数来实现的,可以在异步操作结束后调用这两个函数改变 Promise 实例的状态
- resolve()执行后,调用then方法;reject()调用catch方法
async / await 异步函数
概念理解
- 执行异步函数async函数,返回的都是Promise对象
- 在异步函数中使用await指令,可以简化Promise、then这一系列过程,async 函数需要等待await后的函数执行完成并且有了返回结果(Promise对象)之后,才能继续执行下面的代码
- 在await 指令后必须跟一个 Promise
1 | async function f1() { |
- 使用try…catch来捕获await抛出的异常
1 | async function f1() { |
await阻塞示例
在下面的代码中,await等待了一个Promise对象的返回,并在等待的过程中阻塞了后面的代码。
console.log("start")、test1() 、console.log("end")
是视为同步代码进行的
1 | async function test1() { |
在没有 await
的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。
1 | async function test1() { |
async/await对比Promise的优势
- 代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调⽤也会带来额外的阅读负担
- Promise传递中间值⾮常麻烦,⽽async/await⼏乎是同步的写法,⾮常优雅
- 错误处理友好,async/await可以⽤成熟的try/catch,Promise的错误捕获⾮常冗余
应用举例
假如函数B需要使用函数A返回的值来进行调用,但可能由于受到网速的影响使得B被调用时还没有拿到A的返回值,导致调用失败,这时候也需要异步处理。
1 | function A() { |
Event Loop
Event Loop是什么
在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")。
Event Loop执行顺序
- 先顺序执行同步代码、宏任务
- 同步栈为空,查询是否有异步代码需要执行
- 循环访问callback队列,执行所有微任务
- 执行完,是否需要渲染页面