前言
如题,在下只是一名不知名的后端小喽啰,之前也不懂为啥前端经常可以看到一堆 async 和 await,再或者是 promise。直到最近使用AI开发项目,JS代码是真的看得一愣一愣的😅。看来这年头,单干的独立开发,确实是要懂点前端啊。
有道是:君子生非异也,善假于物也。人要善于学习,不懂就学习嘛,而学习现在最便利的莫过于问AI了,AI多数时候真可以是一位知识渊博的老师,可以一路问问题问下去而不厌其烦地回答你。

进入正题
promise 、async 和 await 的演进过程,其实可以简单的理解为前端 JavaScript 异步编程不断进化的过程,每个阶段都是对前一个阶段问题的改进。
promise有什么用
因为是先有 promise 后有 async 和 await。所以先从介绍 promise 开始哈。
根据马克思主义,一般新事物的出现是为了替代旧事物的存在,因而想知道 promise 有什么用,那么自然得看没有这货之前的前端是咋样的。
javascript
function step1(a, callback) {
console.log('Step 1:', a);
callback(a + 1);
}
function step2(b, callback) {
console.log('Step 2:', b);
callback(b * 2);
}
function step3(c, callback) {
console.log('Step 3:', c);
callback(c - 3);
}
step1(5, (result1) => {
step2(result1, (result2) => {
step3(result2, (result3) => {
console.log('Final Result:', result3);
});
});
});
参考AI的回答,写了上述0几年前端写回调的场景代码。
上述代码理解也很简单,就三个步骤,采用回调方式,每一步骤的结果作为后置步骤的输入,但是弊端是执行回调链路过于复杂。在实际执行时,如果执行步骤再多些,这个调用链路就该抓狂了,甚至于这里简化的调用如果加上判断每个步骤的异常报错情况,3步骤也很恐怖......
而在现代化的前端交互中,多步骤回调太稀疏平常了,就一个购物车结算流程:验证库存→计算价格→创建订单→处理支付→发送确认通知,都5步骤啦。
好了,再来看看新事物: promise 版本。
javascript
function step1Promise(a) {
return new Promise((resolve) => {
console.log('Step 1:', a);
resolve(a + 1);
});
}
function step2Promise(b) {
return new Promise((resolve) => {
console.log('Step 2:', b);
resolve(b * 2);
});
}
function step3Promise(c) {
return new Promise((resolve) => {
console.log('Step 3:', c);
resolve(c - 3);
});
}
step1Promise(5)
.then(result1 => step2Promise(result1))
.then(result2 => step3Promise(result2))
.then(result3 => {
console.log('Final Result:', result3);
});
看见没,实际执行时,已经不再是疯狂嵌套了,而是链式调用,这种明显优雅且维护性提高了不止一星半点。
也就是说:没有 promise 前,前端的嵌套回调场景,可能会是一个地狱 。而 promise 的出现,正是为了解决这一问题,让你活得更久。
那async又有什么用
回答这个问题前,来看看下述代码,猜猜返回啥。
javascript
async function testAsync() {
return 'hello world';
}
console.log(testAsync());
可能和你预期的不大一样,这里打印的不是 hello world,而是 Promise {<fulfilled>: 'hello world'} 。如果想获取实际结果,要这样:
javascript
async function testAsync() {
return 'hello world';
}
testAsync().then(result => console.log(result));
也就是说实际上,async 本质上是一个封装了的 Promise.resolve() ,相当于简化操作。
再看看继 promise 后的新兴事物: async 版的多层回调,是不是进一步简化了。
javascript
async function step1Async(a) {
console.log('Step 1:', a);
return a + 1;
}
async function step2Async(b) {
console.log('Step 2:', b);
return b * 2;
}
async function step3Async(c) {
console.log('Step 3:', c);
return c - 3;
}
step1Async(5)
.then(result1 => step2Async(result1))
.then(result2 => step3Async(result2))
.then(result3 => {
console.log('Final Result:', result3);
});
再说await有什么用
还是一样,先上代码。
javascript
async function testAsync() {
return 'hello world';
}
testAsync().then(result => console.log(result));
console.log(`${await testAsync()}`);
你会发现,两种方式返回的是一样的效果,说白点,也就是 async 方法使用 await 可以直接获取到 promise.resolve() 中的值,也就是实际上 await 可以简单的理解为又是一个简化操作。
✍️冷知识:
await其实不仅可以用于async,也可以用于非async的方法,只是在非async方法中使用会直接返回方法return的数据。
可能你会问:那我用一个 promise 就能实现的功能,为啥要多此一举,专门弄这 async& await 两关键词呢?
说到底,跟有 promise 和没有 promise 有什么区别?其实是一个道理,都是为了更优雅更舒服地写代码,甚至更方便地调试代码。来看看使用 async&await 版本的多层回调。
javascript
async function step1Async(a) {
console.log('Step 1:', a);
return a + 1;
}
async function step2Async(b) {
console.log('Step 2:', b);
return b * 2;
}
async function step3Async(c) {
console.log('Step 3:', c);
return c - 3;
}
const result1 = await step1Promise(5);
const result2 = await step2Promise(result1);
const result3 = await step3Promise(result2);
console.log('Final Result:', result3);
是不是可读性更佳,看着更像同步代码,调试代码啥的会更容易呢?
可能感知不是很明显,来看看某篇大佬文章的示例代码。
javascript
function step1(n) {
console.log(`step1 with ${n}`);
return takeLongTime(n);
}
function step2(m, n) {
console.log(`step2 with ${m} and ${n}`);
return takeLongTime(m + n);
}
function step3(k, m, n) {
console.log(`step3 with ${k}, ${m} and ${n}`);
return takeLongTime(k + m + n);
}
promise 方式:
javascript
function doIt() {
console.time("doIt");
const time1 = 300;
step1(time1)
.then(time2 => {
return step2(time1, time2)
.then(time3 => [time1, time2, time3]);
})
.then(times => {
const [time1, time2, time3] = times;
return step3(time1, time2, time3);
})
.then(result => {
console.log(`result is ${result}`);
console.timeEnd("doIt");
});
}
doIt();
async/await 方式:
javascript
async function doIt() {
console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time1, time2);
const result = await step3(time1, time2, time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");
}
doIt();
是不是体验差距更明显了呢😁
补充说明
异步编程思想
虽然上述说明的示例代码都是同步操作的,但是大家要理解 async 和 await 从字面意思上来说更多的时候是用于 JavaScript 异步编程场景哈。
💡题外话:实际上,
JavaScript语言本身是单线程模型,如果都采用同步调用,那么遇到阻塞性的操作时,如耗时的后端请求,那么前端页面的JavaScript交互渲染会在阻塞过程中直接卡死,完全用不了那种卡死。因而很多场景下你看到的前端代码都是需要采用异步回调的,这也就是为啥能经常看到代码中各种async和await。
IIFE(立即执行函数表达式)
某些场景下,无法直接在顶层执行 await 函数时,可以考虑使用 IIFE 方式执行呢。如下示例:
javascript
(async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const data = await response.json();
console.log('API响应:', data);
} catch (error) {
console.error('错误:', error);
}
})();
JS无法将异步操作真正变为同步
前些天,刚开始咨询 AI 的时候,我也理解岔了,还自信发现新大陆似的去知乎发想法了,真丢人。

要注意即便将代码调整为 await ,也不是真正的同步操作。按照 JavaScript 的事件处理机制:
javascript
// 事件循环的工作原理
while (true) {
// 1. 执行所有同步任务
// 2. 检查微任务队列(Promise.then, async/await)
// 3. 检查宏任务队列(setTimeout, setInterval, I/O)
// 4. 渲染(如果是在浏览器中)
// 5. 重复
}
在执行 await 的时候,会立即返回 promise 到主线程,继续同步操作。而异步操作的 async 方法,会在所有同步任务执行完毕后,再陆续执行。下述是示例代码,仅作为补充说明:
javascript
async function example() {
console.log('1. async函数开始');
// await 实际上:
// 1. 暂停当前函数的执行
// 2. 将函数剩余部分包装为微任务
// 3. 立即返回一个 Promise
const promise = fetch('/api/data');
console.log('2. 创建了Promise,但未等待');
const data = await promise; // 暂停点
console.log('4. 恢复执行,收到数据');
return data;
}
console.log('A. 调用async函数前');
const resultPromise = example(); // 立即返回Promise,不阻塞
console.log('B. async函数已调用,获得Promise对象');
resultPromise.then(data => {
console.log('C. async函数最终完成');
});
console.log('D. 继续执行其他同步代码');
// 输出顺序:
// A. 调用async函数前
// 1. async函数开始
// 2. 创建了Promise,但未等待
// B. async函数已调用,获得Promise对象
// D. 继续执行其他同步代码
// 4. 恢复执行,收到数据
// C. async函数最终完成
好啦,以上就是今天分享的内容,感谢阅读,欢迎三连!