JavaScript 异步编程—Promise 与 async、await
异步 允许一个任务启动后,程序继续执行其他任务,而无需等待这个任务完成,它与同步是相对的概念
异步编程的特点:
- 任务可以并发执行,程序不会因为等待某个任务而被阻塞,这样可以避免页面冻结或卡顿
- 当异步任务完成时,可以通过回调函数、Promise 或
async/await
的方式来处理结果 - 异步编程更适合处理 I/O 操作,如 网络请求、文件或数据库操作等耗时任务…
Promise 对象
Promise 是一种用于处理异步操作的对象,它表示一个尚未完成但将在将来可能会完成的操作,它可以在异步操作完成后传递结果(成功或失败),从而让开发者能够避免“回调地狱”并使异步代码更易于管理和阅读
回调函数( callback ):
回调函数是一个被作为另一个函数参数传递的函数,它是异步编程的一种方式,在异步操作完成时会调用这个回调函数
例:
1 | /* 例1 */ |
回调地狱( callback hell ):
回调地狱是指在异步编程中,嵌套过多的回调函数导致代码结构复杂、难以阅读和维护的情况。它通常出现在 js 中使用回调函数处理异步操作时,由于需要嵌套多个异步操作,每个操作的结果可能依赖于上一个操作,代码逐渐向右缩进,形成像“金字塔”一样的结构
在 Web 开发中最常见的异步操作就是发起网络请求。当你发起网络请求时,你可以传递一个回调函数,当请求响应时,这个回调函数会被调用
例:
1 | // 假设我们有几个异步网络请求操作需要按顺序执行,比如:获取用户数据、获取用户的订单、获取订单的详细信息。使用纯回调的写法大致如下 |
Promise 基本概念
Promise 有三种状态:
Pending(待定):初始状态,表示异步操作既没有完成也没有失败
Fulfilled(已完成):表示异步操作完成,并返回了一个结果( value / result )
Rejected(已拒绝):表示异步操作失败,并返回了一个原因或错误( reson / error )
Promise 的状态一旦变为 fulfilled 或 rejected,就不会再改变
创建 Promise
语法:
1 | const promise = new Promise(executor) |
例:
1 | // 创建一个 Promise 类型对象 |
resolve()
:
resolve
函数代表异步操作成功时会调用的函数,我们可以将异步操作成功的结果,作为参数传递出去
resolve
函数在调用时,会将 promise 的状态从 Pending 修改为 Fulfilled,此时 promise 的状态和结果就会固定,其它任何操作都无法再次修改 promise 的状态
reject()
:
reject
函数代表异步操作失败时( 比如 抛出错误 )会调用的函数,我们可以将异步操作错误的结果,作为参数传递出去
resolve
函数在调用时,会将 promise 的状态从 Pending 修改为 Rejected,此时 promise 的状态和结果就会固定,其它任何操作都无法再次修改 promise 的状态
promise.then()
:
then
方法的参数中包含两个函数,第一个是 promise 状态为 Fulfilled 时执行的函数,第二个是 promise 状态为 Rejected 时执行的函数,它们都可以接受参数来获取成功和失败的结果
Promise 静态方法
Promise.resolve(value)
:
用于直接返回一个状态为 Fulfilled 的 Promise 类型对象
Promise.reject(error)
:
用于直接返回一个状态为 Rejected 的 Promise 类型对象
Promise.all()
:
Promise.all()
用于处理多个 Promise
类型对象。Promise.all()
可以接收多个 Promise 类型对象作为输入,当所有输入的 Promise 类型对象的状态都为 Fulfilled 时,则返回一个状态为 Fulfilled 并且包含多个结果的 Promise 类型对象
例:
1 | const promise1 = Promise.resolve(1); |
Promise.race()
:
Promise.race()
用于处理多个 Promise
类型对象,并返回第一个完成( 成功或失败 )的 Promise
类型对象
即那个异步操作最先完成( 成功或失败 )就处理那个,其它没有完成的就不等了
Promise 链式调用
Promise 链式调用是指多个 Promise 类型对象通过 .then()
方法串联在一起的操作方式,从而避免了回调地狱的问题
每个 .then()
方法都会返回一个新的 Promise 类型对象,从而可以继续链式调用
你也可以手动返回一个 Promise 类型对象,如果手动返回的是一个非 Promise 类型对象,则也会将其包装为 Promise 类型对象返回
例:
1 | new Promise((resolve, reject) => { |
用 js 手写 Promise
- 错误处理:Promise 提供了链式的
.catch()
方法,可以集中处理错误,避免在每个回调中重复处理错误