掌握异步编程:JavaScript异步处理完全指南

发表时间: 2024-04-30 23:31

引言

本文将介绍异步编程中的几种方式,将会以JavaScript为例子,帮助读者快速了解掌握异步编程。


何为异步编程

在日常开发中,我们总会遇到一些需要处理大量io或者是计算任务,由于JavaScrpit采用单线程模型,这些任务会将JS线程卡住,导致出现没有反应,影响用户体验。异步编程的作用通俗的来讲: 异步编程技术使你的程序可以在执行一个可能长期运行的任务的同时继续对其他事件做出反应而不必等待任务完成。与此同时,你的程序也将在任务完成后显示结果。

回调函数

回调函数曾经是JavaScript最常用的异步方式,然而,当回调函数本身需要调用其他同样接受回调函数的函数时,基于回调的代码会变得难以理解。当你需要执行一些分解成一系列异步函数的操作时,这将变得十分常见。例如下面这种情况:

将其写成异步的结果如下:

可以看到,因为必须在回调函数中调用回调函数,我们会得到一个深层嵌套的doOperation函数,这个大型项目中尤为严重,可能会造成6~7层的嵌套,导致所谓的回调地狱(CallBack Hell)代码可读性会非常的差,因此不推荐此种方式编写异步函数。


Promise

Promise是在es6(ECMAScript 2015)新增加的特性之一,用于解决以往异步编程中产生的回调地狱问题。Promise表示一个异步操作的最终完成(或失败)及其结果的值。Promise可以看作是对一个尚未完成的操作的承诺,它会在未来的某个时间点返回一个结果。

Promise对象有三种状态:

  • Pending(进行中): 初始状态,表示操作尚未完成,仍然处于进行中。
  • Fulfilled(已完成):表示操作已经成功完成,并且有一个确定的值。
  • Rejected(已失败):表示操作因某种原因失败,且有一个确定的原因(错误)。

Promise最典型的案例是fetch函数,调用fetch函数会返回一个Promise对象,它代表着在未来某一刻会返回数据,我们还可以调用.then方法传递回调函数,如果这个请求成功完成,这个回调函数就会被调起,请求结果将以参数的方式,传递给回调函数。

例子如下:

Promise优点是可以将多个异步处理任务通过链式调用串联起来,解决了回调函数的多层嵌套,提高代码可读性,除此以外,Promise还有.catch方法和.finally方法,分别用于捕获错误并终止其他回调函数执行,和在Promise执行完毕后调用的操作,无论失败与否。


我们还可以自己创建一个Promise函数,例子如下:

其中setTimeout函数,在两秒后执行异步操作,resolve, reject分别代表着Promise已完成或失败这两种状态并进行相应的操作。

Async/Await

Async/Await是基于Promise出现的一个语法糖,可以让异步操作更加的简洁明了,首先我们需要使用 async 关键字讲函数标记为异步操作(注意:这里的异步函数是指返回值为 Promise 对象的函数),在异步函数中我们可以调用其它的异步函数,不过我们不再需要使用 then ,而是使用 关键字 await。await 会等待 Promise 完成之后再接返回最终的结果,await虽然看上去会暂停函数的执行,但在等待的过程中 javaScript 同样可以处理其它的任务,比如更新界面等等。这是因为await 底层是基于Promise和事件循环机制实现的。

例子如下:

但await也有其不足,主要表现为以下3点:

  1. 必须等待第一个任务执行完成之后才会开始第二个任务, 所以解决办法是将所有 Promise 用 Promise.all 组合起来:

  1. 在循环中执行异步操作不能直接调用 forEach 或者 map 这一类的方法。
  2. 不能在全局或普通函数中直接使用 await 关键字,其只能在异步函数中使用。如果我们想要在最外层使用 await,则需要先定义一个异步函数,然后在函数体中使用它:

最后祝大家学习愉快!欢迎一起讨论学习,如果有错误,请在评论区指出来。原创不易点个赞呗