async/await
之所以能让异步代码呈现出 "同步的写法和执行体验",核心在于它是 Generator 函数和 Promise 的语法糖,通过语法层面的封装,隐藏了异步操作的回调嵌套,让代码结构更接近同步逻辑。
具体来说,它的 "同步效果" 体现在两个方面:
1. 写法上的同步化
传统异步代码(如 Promise 的 then
链式调用或回调函数)需要嵌套或链式书写,而 async/await
允许用 线性代码结构 编写异步逻辑,就像写同步代码一样:
javascript
ini
// 传统 Promise 写法(链式调用)
fetchData()
.then(data => {
return processData(data);
})
.then(result => {
console.log(result);
});
// async/await 写法(线性结构)
async function handleData() {
const data = await fetchData();
const result = await processData(data);
console.log(result);
}
后者的代码流程和 "同步读取数据→处理数据→输出结果" 的逻辑完全一致,更符合人类的思维习惯。
2. 执行上的 "等待" 效果
await
关键字会 暂停当前函数的执行 ,等待后面的 Promise 状态变为 resolved
后,再继续执行函数内后续的代码。
这种 "暂停等待" 的特性,让异步操作看起来像 "同步阻塞" 一样:
-
当执行到
const data = await fetchData()
时,函数会暂停,等待fetchData()
返回的 Promise 完成。 -
只有当 Promise 成功解析(拿到数据)后,才会把结果赋值给
data
,然后执行下一行processData(data)
。
注意 :这种 "暂停" 是 非阻塞的 ------JavaScript 引擎在等待 Promise 期间,会去处理其他任务(如浏览器事件、定时器等),不会卡住整个线程。这和真正的同步阻塞(如 alert()
)完全不同。
底层原理
async/await
的本质是利用了:
-
Promise 的异步状态管理 :
await
后面必须跟一个 Promise(或可被转换为 Promise 的对象),依赖 Promise 来管理异步操作的完成 / 失败状态。 -
Generator 的暂停 / 恢复机制 :
async
函数会被 JavaScript 引擎转换为类似 Generator 的结构,await
对应 Generator 中的yield
,实现函数执行的暂停和恢复。
简单说,async/await
并没有改变 JavaScript 异步单线程的本质,只是通过语法糖让异步代码的 结构更清晰、逻辑更接近同步,从而降低了异步编程的复杂度。
Generator 是 JavaScript 中一种特殊的函数类型,它的核心特点是可以暂停执行和恢复执行,通过这种 "分段续" 能力,能够更灵活地控制函数的执行流程。
Generator 函数的结构有以下几个关键特征:
1. 定义方式
通过在 function
关键字后加 *
来声明(function*
),函数内部使用 yield
关键字标记暂停点:
javascript
javascript
function* myGenerator() {
yield '第一个值';
yield '第二个值';
return '结束值';
}
2. 执行机制
调用 Generator 函数不会立即执行函数体 ,而是返回一个迭代器对象(Iterator) ,通过迭代器的 next()
方法控制函数执行:
javascript
lua
const generator = myGenerator(); // 不会执行函数体,返回迭代器
// 第一次调用 next():执行到第一个 yield 处暂停,返回 yield 的值
console.log(generator.next()); // { value: '第一个值', done: false }
// 第二次调用 next():从暂停处继续执行到下一个 yield 处暂停
console.log(generator.next()); // { value: '第二个值', done: false }
// 第三次调用 next():从暂停处继续执行到 return(或函数结束)
console.log(generator.next()); // { value: '结束值', done: true }
// 后续调用 next():函数已执行完毕,返回 undefined
console.log(generator.next()); // { value: undefined, done: true }
yield
:相当于 "暂停标记",执行到此处会暂停,并将其后的值作为next()
返回对象的value
。done
:布尔值,表示函数是否执行完毕(false
未完毕,true
已完毕)。
3. 可注入数据
通过 next(arg)
可以向 Generator 函数内部注入数据,作为上一个 yield
的返回值:
javascript
lua
function* dataGenerator() {
const input1 = yield '请输入第一个值';
const input2 = yield '请输入第二个值';
return `你输入了:${input1} 和 ${input2}`;
}
const generator = dataGenerator();
console.log(generator.next()); // { value: '请输入第一个值', done: false }
console.log(generator.next('hello')); // { value: '请输入第二个值', done: false }(input1 = 'hello')
console.log(generator.next('world')); // { value: '你输入了:hello 和 world', done: true }(input2 = 'world')
4. 应用场景
Generator 的 "暂停 - 恢复" 特性使其适合处理:
-
异步操作流程控制(
async/await
的前身思路) -
迭代器生成(如自定义可迭代对象)
-
状态机(如有限状态的分步执行)
简单说,Generator 函数通过 function*
定义,用 yield
标记暂停点,返回的迭代器通过 next()
控制执行,从而实现了函数执行的 "分段控制",这也是它区别于普通函数的核心特性。