异步编程

511 阅读6分钟

异步编程的由来

JavaScript语言的执行环境是的是单线程的,一次只能执行一个任务,多个任务需要排队等候,这种模式会出现阻塞代码,导致代码执行效率低下,,会造成页面卡顿,影响用户的体验度;为了避免这个问题,提出了异步编程;异步编程的方式有:callback回调函数事件监听promise 等;

JavaScript执行模式

  • 同步模式
    • javascript代码是 依次执行 ,后面代码要等待前面代码执行完毕后,才能执行;在单线程中,代码大多数是以 同步模式 进行执行的,存在很严重问题,其他某一个任务执行时间过长,那么后面的任务会延迟,这种延迟被称为阻塞,对用户而言就会出卡顿或则会卡死,所以就需要异步模式来解决程序当中无法避免的耗时操作;
  • 异步模式
    • javascript代码 不用等待 前面的代码执行完毕才开始执行,耗时任务的后续逻辑一般会通过回调函数来执行,因为单线程无法同时处理多个文件,单线程模式下的异步它最大的难点就是代码的执行顺序混乱;
    • 我们将执行的代码放入调用栈中执行,如果是同步的直接执行,如果是异步代码放入Queue中等后同步代码执行完毕;
    • EventLoop它会 监听调用栈Queue 中的任务
    • 当调用栈中所有的任务结束以后,它会从Queue中取出一个任务放进调用栈中,开始执行,等待所有循环结束

异步环境组成:

web APIs环境, Queue消息队列,Call stack调用栈:正在执行的工作表, Console打印 ,EventLoop:负责监听call stack 和 Queue

  • 消息队列(Queue): Queue是只暂时存储异步任务的地方
  • 宏任务
    • 当前调用栈中执行的代码成为宏任务;浏览器为了让JS内部宏任务与DOM操作能有有序的执行,会在一个宏任务执行后,在下一个宏任务执行开始前,对页面进行重新渲染;
    • 宏任务包含:setTimeout、setInterval、I/O、UI交互事件
  • 微任务
    • 当前任务执行结束后需要立即执行的任务,不用进入Queue等候执行,它是在页面渲染之前执行,速度相比宏任务更快;
    • 微任务包含:Promise、MutaionObserver、process.nextTick(‘Node.js’)环境)等

浏览器执行引擎先去执行Call stack的任务,通过Event Loop从queue中去一个任务出来去执行。整个任务过程中,我们随时可以忘消息队列中放入一些任务,这些任务会Queue中排队等候循环。 image.png

  • 回调函数:所有异步编程方案的根基(可以理解为一件你想要做的事情),有调用者定义,交给执行者执行的函数,称之为回调函数;

Promise

CommonJS社区提出了Promise规范。解决回调 “地狱”,尽可能将原来嵌套的回调变为扁平化处理。

Promise的基本使用

Promise是一个全局类型,构造函数接受 函数参数,在函数内部能够接受两个参数(resolve,reject),一般异步任务通过resolve参数传递出去;错误的话,使用reject传递出去;状态一旦确定不能去修改;

image.png

promise同步代码先执行完毕,再去执行promise.then()的回调函数

image.png

Promsie常见误区:

嵌套使用的方式是promise的错误使用 image.png 借助于promise then方法链式调用的特点,尽量去保证异步任务的扁平化。 每一个then方法实际上是为上一个then返回的promise对象添加状态明确过后的回调,那这些promise会依次执行。而且也可能在promise的对象中手动回调。

  • promise对象的then方法会返回一个全新的promise对象
  • 后面的then方法就是为上一个then方法返回的promise注册回调
  • 前面then方法中回调函数的返回值会作为后面then方法回调的参数
  • 如果回调中返回的是promise,那后面then方法的回调会等待它的结束

promise异常处理

catch方法注册失败回调 image.png

  • promise失败或出现异常会执行onRejected注册的回调;
  • then方法的第二个参数,缺点:无法捕获到本轮then方法以及下层的异常;
  • catch方法:推荐使用;在链式调用的情况下使用catch,因为promise的链条上任何的异常都会向后传递,然后catch就会捕获到异常

promise静态方法

image.png 有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用 Promise.resolve()

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
  • 可以将普通值快速转换为promise对象
  • 如果传递的为promise,会将这个promise原样返回
  • 如果传递的是一个对象,并且带有then方法,第一个参数为onFulfilled,第二个参数为onRejected,也可以被当做promise继续向下执行。这种对象实现了thenable的接口。

promise并行执行

  • promise.all() 等待所有任务结束才结束
    • 接受一个数组
    • 数组中可以是promise对象,也可以是普通值
    • 只要所有的promise对象都成功结束,才会结束。并返回所有promise实例
    • 只要有一个promise失败,则promise失败,返回第一个失败的promise的结果
  • promise.race()
    • 只要有一个任务完成,就会返回promise的值

promise执行时序

  • 微任务 提高整体的响应能力,不用等候
  • 宏任务:回调对了(Queue)中的任务称之为【宏任务】,在宏任务执行过程中可以临时加上一些额外需求,额外需求可以选择作为一个新的宏任务进到队列中排队。也可以作为当前任务的【微任务】,直接在当前任务结束过后立即执行不同排队。

特殊:promise的回调会作为微任务执行,MutationObserver和process.nextTick作为微任务

async/await 语法糖

async是 Generator 函数的语法糖 async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句

async function main() {
    const user = await ajax('')
    console.log(user)
    const data = await ajax('')
    console.log(data)
}