summary
type
category
tags
slug
status
date
finished_date
icon
password
什么是异步编程
假设你正在准备晚餐,如果以一种同步的方式工作:你必须等鸡肉烤好后才能去煮意大利面。
但为了提高效率,可以通过使用异步操作:将鸡肉放进烤箱开始烤后设置一个计时器,并在在等待鸡肉的过程中,开始准备意大利面。
为什么 JS 需要异步编程
- JS 最初被设计为在浏览器中运行,它是单线程的,这意味着它一次只能执行一个任务。在执行长时间操作(如网络请求、文件读写等)时,整个程序将会停滞,直到操作完成。
- 为了避免阻塞这个线程,JS 使用了事件循环,允许非阻塞的操作,即异步编程的核心。
什么是事件循环
Event Loop
一种用来管理多个事件和异步操作的机制
同步和异步任务会进入不同的执行环境
- 同步的进入主线程,即主执行栈
- 异步的进入任务队列
任务队列中的任务又分为两种
宏任务
Macro-tasks
- 定时器事件:
setTimeout
、setInterval
、setImmediate
- 渲染事件:解析 DOM、计算布局、绘制
- 交互事件:如鼠标点击、键盘事件
- I/O 操作:网络请求、文件读写
微任务
Micro-tasks,优先级高于宏任务
Promise
的.then()
、.catch()
和.finally()
回调(📦详见下文)
async/await
语法中的await
关键字后面的代码
MutationObserver
监听 DOM 变化时的回调函数
- Node.js 的
process.nextTick
事件循环的每个循环迭代称为一个 tick。在每个 tick 中,以下步骤会被执行:
- 执行一个宏任务(从宏任务队列中取出下一个任务执行)
- 执行所有排队的微任务(在这个 tick 中,每当有微任务被添加,它们就会被立即执行,直到微任务队列为空)
这段代码的执行顺序序号是 1>3>2。
即使延迟时间为 0,回调也只能在所有同步代码执行完毕后。
什么是回调函数
在 JS 中,函数是一等公民,意味着可以像其他数据类型一样处理函数:可以将函数作为参数传递给其他函数,也可以作为值从函数返回。回调函数是作为参数传递给另一个函数的函数。
When you pass a function as an argument, remember not to use parenthesis.
- Right: calculator(5, 10, calc);
- Wrong: calculator(5, 10, calc());
Promise 出现前,JS 的异步编程依靠回调。回调函数容易导致代码结构混乱,产生“回调地狱” (Callback Hell)。
什么是 Promise
可以将 Promise 想象成一个未来某个时刻可能得到的结果的承诺,它有三种状态
- Pending(待解决):初始状态。
- Fulfilled(已兑现):操作成功。
- Rejected(已拒绝):操作失败。
基本构造
链式调用
适合处理简单的异步操作,或者当需要更精细的控制 Promise 链。
then
方法最多可以接受两个参数(第二个参数只捕获到它之前的 Promise 错误)
catch
能够捕获之前所有 Promise 链中的错误
链式调用怎么实现的?
- 每个
.then()
方法都返回一个新的 Promise 对象,这个新的 Promise 对象可以用来连接下一个.then()
调用,这样就形成了链式调用。
- Promise 内部使用了微任务来异步执行
.then()
方法的回调函数。这意味着每个.then()
中的回调函数会等待当前 JS 执行栈清空后执行,这样可以确保异步操作按照正确的顺序执行。
Async 与 Await
ES8 /ES2017 中引入,建立在 Promise 之上的语法糖,允许以同步的方式编写异步代码。提供了更简洁和直观的方式来处理复杂的异步操作,特别是涉及多个步骤或条件语句。
async
函数:函数会隐式地返回一个 Promise。
await
关键字:在异步函数内使用,它会暂停函数的执行,等待 Promise 的解决。
Generator
async/await
内部使用生成器函数来实现异步流程的控制 → Generator 允许函数的执行在某一点挂起(通过 yield
关键字),然后在稍后的某个时候恢复执行处理并发
Concurrency methods
Promise.all()
当有多个异步操作需要执行,并希望等待它们全部完成时,它允许同时处理多个 Promise 对象,然后返回一个 Promise 对象。
Promise.allSettled()
:类似于 Promise.all,但它等待所有的 Promise 完成,无论是解决还是拒绝,并返回一个对象数组,每个对象表示对应的 Promise 的结果。
Promise.race()
:在一组 Promise 中的任何一个解决或拒绝时就解决或拒绝,返回一个 Promise。
Promise.any()
:返回一组 Promise 中第一个解决的结果。如果所有 Promise 都拒绝,它也拒绝。
Promise.resolve()
和Promise.reject()
:用于快速创建一个状态为解决或拒绝的 Promise。
一些缺点
- 不可取消:如果有一个长时间运行的异步操作,无法中途停止。
- 错误处理:如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
- 单次解决: Promise 只能解决或拒绝一次,它们无法处理重复发生的事件。