还在找什么,JavaScript的异步编程解决方案全在这里了

还在找什么,JavaScript的异步编程解决方案全在这里了
最新回答
雨季盛开的花

2023-03-24 04:31:55

JavaScript 的异步编程解决方案主要包括 Callback、Promise、Generator、Async/Await,以下是对这些方案的详细介绍:

单线程异步原理

JavaScript 是单线程语言,同一时间只能执行一段代码,阻塞其他代码执行。异步操作(如网络请求、IO 操作、定时函数)由浏览器多线程协作完成。例如,异步请求时,JS 执行线程发起请求,浏览器开启新线程执行请求,完成后将回调函数插入 JS 执行队列尾部等待处理。其执行流程如下:

  • 所有同步任务在主线程执行,形成执行栈。
  • 主线程外存在任务队列,异步任务有结果时在队列中放置事件。
  • 执行栈中同步任务执行完毕后,系统读取任务队列,将对应异步任务结束等待状态,进入执行栈执行。
  • 主线程不断重复上述步骤。

通过定时函数实例可看出异步处理过程,先打印 1,执行 setTimeout 约定 0 秒后打印 2,再打印 3,最终执行结果为先打印 1,再打印 3,最后打印 2,说明发生了异步过程。

异步解决方案Callback

回调函数是最早的异步解决方案,当事件完成后执行回调函数实现异步操作。函数可作为参数传递给另一个函数,在另一个函数中调用。例如发送 Ajax 异步请求,多个请求嵌套时,回调函数会形成多级嵌套,代码呈金字塔型结构。虽然能解决异步问题,但代码难懂,调试和重构风险高。

以 mongoDB 在 node.js 中的实例为例,嵌套了六层,当多个异步事务多级依赖时,这种结构弊端明显。

Promise

Promise 是更合理强大的异步解决方案,具有以下特点:

  • 三种状态:等待(pending)、已完成(resolved)、已拒绝(rejected)。
  • 状态转换:只能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换。
  • then 方法:promise 对象必须实现 then 方法,且 then 必须返回一个 promise,同一个 promise 的 then 可以调用多次(链式),回调执行顺序与定义顺序一致。
  • 参数接受:then 方法接受两个参数,第一个是成功时的回调,在 promise 由“等待”态转换到“完成”态时调用;另一个是失败时的回调,在 promise 由“等待”态转换到“拒绝”态时调用。

左侧创建 promise 对象,隔 2000 秒后执行,最后返回 promise 对象。Promise 有立即执行性,创建完对象后,在已完成或已拒绝之前代码都会执行一遍。右侧为打印结果,可见创建 promise 对象被打印出来,并成功生成 promise 对象,执行 then 时,因已返回成功状态,所以一直执行成功的回调函数。

Promise 解决异步问题的特点如下:

  • 代码更符合逻辑、可读性更强。
  • 未改变 JS 异步执行本质,写法上能看到一点 callback 的影子。

链式调用方式中,第一个请求成功后执行下一个,若任何一个请求未成功,直接 catch 掉 error 并打印。

Generator

Generator 函数可实现以同步方式写异步代码,使逻辑更清晰,代码量减少。其特质如下:

  • 定义:使用 function* 定义。
  • 生成对象:使用时生成一个 Generator 对象。
  • 执行与暂停:执行.next() 激活暂停状态,开始执行内部代码,直到遇到 yield,返回此时执行结果,并记住执行上下文,暂停;再次执行.next() 时重复此步骤。

首先定义 test = add(5),函数未执行,只是生成 generator 对象,函数处于暂停状态。执行.next() 时,激活暂停状态,开始执行内部代码,直到遇到第一个 yield 返回执行结果,并记住上下文,暂停,交出控制权。再次执行.next() 时,找到第二个 yield,再次记住上下文,暂停,交出控制权,依此重复。

封装异步任务时,定义 generator 对象,执行请求后交出控制权,通过 promise 判断是否继续执行。Generator 需要自动执行器配合使用,实现正常思维下的异步处理,有了自动执行器,异步请求可用同步方式写异步代码,直接将请求全部写在一起。

Async/Await

Async 和 await 函数是进一步演进的结果,个性特征如下:

  • async 标识:async 表示这是一个 async 函数,await 只能在该函数里面使用。
  • await 等待:await 表示在此等待后面的操作执行完毕,再执行下一句代码。
  • 操作类型:await 后面最好是一个耗时操作或者异步操作。
  • Promise 对象:await 后面必须是一个 Promise 对象,如果不是会被转化为一个已完成状态的 Promise。

Async 与 generator 写法类似,本质上是 generator 的语法糖。其内置执行器,具备更好的语义,更广的适用性,并且返回值是 Promise。