面试官看着你,问道:“可以讲讲 async/await 到底是什么吗?”
这是很多前端开发者面试时都会遇到的问题。不少人第一反应是:“就是让异步代码看起来像同步的……”但这样的回答太浅显了。
面试官想知道的不仅仅是定义,而是你是否真正理解它的工作原理、解决了什么问题,以及在实际开发中需要注意哪些细节。
在深入了解之前,我们先看看 JavaScript 异步处理的发展过程。
最早的异步处理使用回调函数:
getData(function(a) {
getMoreData(a, function(b) {
getEvenMoreData(b, function(c) {
getFinalData(c, function(result) {
console.log(result);
});
});
});
});
这种代码层层嵌套,形成了所谓的“回调地狱”,代码难以阅读和维护。
ES6 引入 Promise 后,情况有所改善:
getData()
.then(a => getMoreData(a))
.then(b => getEvenMoreData(b))
.then(c => getFinalData(c))
.then(result => console.log(result))
.catch(err => console.error(err));
Promise 使用链式调用,代码结构更清晰。但在复杂场景下,Promise 仍有不足:
条件判断变得复杂
中间变量传递麻烦
错误处理不够直观
循环处理异步操作很绕
async/await 的出现解决了这些问题,让我们能用同步的写法处理异步逻辑。
先看一个简单例子:
async function fetchData() {
try {
const response = await fetch('/api/user');
const user = await response.json();
console.log(user);
} catch (error) {
console.error('请求出错:', error);
}
}
这里有三个关键点需要掌握:
async 函数总是返回 Promise
async function hello() {
return 'world';
}
hello().then(console.log); // 输出 'world'
如果 async 函数返回一个值,会自动包装成 Promise。如果抛出错误,会变成拒绝的 Promise。
await 只能在 async 函数内部使用
在普通函数中使用 await 会导致语法错误。
await 可以等待任何值
如果 await 后面的不是 Promise,会自动转换成 Promise:
const num = await 42; // 等同于 await Promise.resolve(42)
很多人误以为 await 会阻塞线程,其实不是。await 只是把后续代码注册为微任务,等当前同步代码执行完毕后再执行。
看这个例子:
console.log('1');
async function foo() {
console.log('2');
await Promise.resolve();
console.log('3');
}
foo();
console.log('4');
// 输出顺序:1, 2, 4, 3
await Promise.resolve() 把 console.log('3') 放入微任务队列,先执行完同步代码 console.log('4'),再从微任务队列中取出执行。
并行执行异步操作
错误做法(串行执行):
async function slowExample() {
const a = await fetchA(); // 等待 200ms
const b = await fetchB(); // 再等待 200ms
const c = await fetchC(); // 再等待 200ms
// 总耗时约 600ms
}
正确做法(并行执行):
async function fastExample() {
const [a, b, c] = await Promise.all([
fetchA(),
fetchB(),
fetchC()
]);
// 总耗时约 200ms
}
如果需要处理可能失败的请求,可以使用 Promise.allSettled:
const results = await Promise.allSettled([
fetchA(),
fetchB(),
fetchC()
]);
results.forEach((result) => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.log('失败:', result.reason);
}
});
条件判断中的异步操作
async function loadPage() {
const user = await fetchUser();
if (user.isAdmin) {
const data = await fetchAdminData();
renderAdminPage(data);
} else {
const data = await fetchUserData();
renderUserPage(data);
}
}
这种条件分支的异步处理,用 async/await 写起来很直观。
循环中的异步处理
常见错误:
async function slowLoop() {
const ids = [1, 2, 3, 4, 5];
for (let id of ids) {
await fetchUser(id); // 串行执行,效率低
}
}
改进方法:如果需要并行执行:
async function parallelLoop() {
const ids = [1, 2, 3, 4, 5];
await Promise.all(ids.map(id => fetchUser(id)));
}
如果需要顺序执行:
async function sequentialLoop() {
const ids = [1, 2, 3, 4, 5];
for (let id of ids) {
const user = await fetchUser(id);
process(user); // 确保顺序处理
}
}
忘记错误处理
async function unsafeFetch() {
const res = await fetch('/api/data');
return res.json();
}
// 如果请求失败,错误无法被捕获
正确做法:
async function safeFetch() {
try {
const res = await fetch('/api/data');
const data = await res.json();
return data;
} catch (error) {
console.error('请求失败:', error);
return null;
}
}
在数组方法中误用 await
const urls = ['/a', '/b', '/c'];
urls.map(async (url) => {
const res = await fetch(url);
console.log(await res.text());
});
console.log('done'); // 这行会先执行
map 方法不会等待 async 函数执行完成。应该使用 for...of 循环:
for (let url of urls) {
const res = await fetch(url);
console.log(await res.text());
}
console.log('done'); // 等待所有请求完成
实现重试机制
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
if (i === retries - 1) throw error;
// 等待时间逐渐增加
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
超时控制
async function fetchWithTimeout(url, timeout = 5000) {
const fetchPromise = fetch(url);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('请求超时')), timeout);
});
return await Promise.race([fetchPromise, timeoutPromise]);
}
处理数据流
async function processStream(stream) {
for await (const chunk of stream) {
await processChunk(chunk);
}
}
async/await 让异步代码更易读写,但理解其底层原理很重要。记住几个关键点:
async 函数总是返回 Promise
await 把后续代码放入微任务队列
注意错误处理,使用 try/catch
合理选择串行或并行执行
避免在数组方法中误用 await
掌握这些知识,不仅能通过面试,更能写出健壮的异步代码。在实际项目中,根据具体需求选择合适的异步处理方式,才能发挥 async/await 的最大价值。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!
虽然大家知道async/await,但是很多人对这个方法中内部怎么执行的还不是很了解,本文是我看了一遍技术博客理解 JavaScript 的 async/await
Async实际上是一个封装了自动化执行并返回一个Promise的Generator函数的语法糖。这句话的意思我们可以分为三个部分来解读:首先它有一个自动化执行,Generator函数是依靠不停的调用.net来依次执行的,Async有一个自动化执行的过程
Node.js7.6起, Node.js 搭载了有async函数功能的V8引擎。当Node.js 8于10月31日成为LTS版本后,我们没有理由不使用async函数。接下来,我将简要介绍async函数,以及如何改变我们编写Node.js应用程序的方式。
自2015年11 发布1.7版以来,TypeScript 已支持 async/await 关键字。编译器使用 yield 将异步函数转换为生成器函数。这意味着咱们无法针对 ES3 或 ES5,因为生成器仅在 ES6 中引入的。
async/await 大家肯定都用过,在处理异步操作的时候真的是很方便。那今天主要讲一些在使用 async/await 时容易忽略和犯错的地方。上面的代码中,每一行都会 等待上一行的结果返回后才会执行。
有一个图片列表,我想要在图片onload成功之后获取加载成功的图片列表,图片资源加载为异步,我们使用ES7的async await方式实现,多张图片,是用for循环。
Async 和 Awaiit 是 Promise 的扩展,我们知道 JavaScript 是单线程的,使用 Promise 之后可以使异步操作的书写更简洁,而 Async 使 Promise 像同步操作
最近代码写着写着,我突然意识到一个问题——我们既然已经有了 Promise 和 then,为啥还需要 async 和 await?这不是脱裤子放屁吗?
如果让你手写async函数的实现,你是不是会觉得很复杂?这篇文章带你用20行搞定它的核心。经常有人说async函数是generator函数的语法糖,那么到底是怎么样一个糖呢?让我们来一层层的剥开它的糖衣。
async/await是ES7的写法,可以让非同步call back写法看起来像同步的顺序去执行。以下我们new一个Promise的class并return给一个function
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!