设计师网站pin专业教育网站建设
news/
2025/9/23 9:38:25/
文章来源:
设计师网站pin,专业教育网站建设,怎么看自己的网站是用什么做的,互联网创新创业项目计划书案例使用 async/await 是必须避免的陷阱
如果我们使用过 nodejs#xff0c;那么我们可能已经在 javaSoript 中使用了异步操作。异步任务是一个独立于 JavaSoript 引擎的主线程执行的操作。从本质上讲#xff0c;这就是应用程序功能没有阻塞的 UI 的原因。 nodejs 的单线程性质那么我们可能已经在 javaSoript 中使用了异步操作。异步任务是一个独立于 JavaSoript 引擎的主线程执行的操作。从本质上讲这就是应用程序功能没有阻塞的 UI 的原因。 nodejs 的单线程性质这一点极其重要。 Node.js 利用事件循环来处理所有异步操作保留了用于计算函数的主线程。 假设我们对事件循环有相当的了解。在这种情况下我们会明白当在调用堆栈中发现一个非同步操作时JS会把它放到线程池上线程池将通过 libuv 库异步地执行它。之后 libuv 将执行操作并将其推进到事件队列中。事件队列将被持续监控事件队列中的事件将被提取并在处理异步操作响应的回调函数上执行。这基本上就是 nodejs 如何处理异步操作。
例如我们可以使用 JavaSrispt 中 Promise 建立异步操作。Promise 返回的一个对象代表其进程。
// 返回一个Promise对象
function fetchData() {return new Promise((resolve, reject) {// 使用setTimeout 模拟一个异步操作setTimeout(() {const data Sample Data;const success true; if (success) {resolve(data); } else {reject(Error: Unable to fetch data);}}, 2000);});
}
const fetchDataPromise fetchData();
fetchDataPromise.then(data {console.log(Data received:, data);
})
.catch(error {console.error(error);
});fetchData 方法返回的 Promise 对象包含两种方法then 和 catch。开发者可以在这两个方法中获取结果。
这使 JavaSrhpt 更加强大使我们能够构建实时聊天应用程序和API等应用程序。然而在设计应用程序时使用JavaSrispt异步操作会有一些常见的缺陷,我们必须考虑这些缺陷以便能够实现缓解这些问题的方法。
注意:这些陷阱存在于任何 javascript 框架。
回调地狱
使用基于 Promise 的异步操作的关键问题之一是回调地狱。在这种情况下回调会不断调用 Promise导致回调链。例如
function performAsyncOperation(delay: number, message: string): Promisestring {return new Promise((resolve) {setTimeout(() {console.log(message);resolve(message);}, delay);});
}export async function callbacks() {const delay 1000;const message Hello World;return performAsyncOperation(delay, message).then((value) {performAsyncOperation(delay, value).then((secondValue) {performAsyncOperation(delay, secondValue).then((thirdValue) {performAsyncOperation(delay, thirdValue).then(() {console.log(End The Callback);}).catch(() {console.log(Error);});;}).catch(() {console.log(Error);});;}).catch(() {console.log(Error);});});
}callbacks() 方法 返回一个 performAsyncOperation 并继续添加更多的异步操作。虽然能在生产中发挥完美的作用。但是当我们考虑到可维护性时它将是一个混乱的问题。例如很难看到什么样的回调应用在什么级别。
所以我们如何避免这种情况?
为了修复回调地狱问题我们可以将此转换为 async/await 。所以, 我们看看这个的更新代码 :
function performAsyncOperation(delay: number, message: string): Promisestring {return new Promise((resolve) {setTimeout(() {console.log(message);resolve(message);}, delay);});
}export async function asyncAwait() {try {const delay 1000;let message Hello World;message await performAsyncOperation(delay, message);message await performAsyncOperation(delay, message);message await performAsyncOperation(delay, message);await performAsyncOperation(delay, message);console.log(End The Callback);} catch (error) {console.log(Error:, error);}
}我们已经成功地将回调地狱重构为更清洁的方法它使用async/await这允许我们执行相同的异步代码而我们在早些时候执行了一个更干净的方法。await 意味着每一行代码在收到回复之前等候。如果它返回一个成功的响应它将继续到下一个。但是如果它遇到错误它将跳到公共的catch 整块。这样做可以避免维护多个错误处理程序和使用单个错误处理程序的需要。
同步函数链
我们已经重构我们的代码使用async/await 块来处理多个异步调用。但是现在我们可能会注意到这里有一个新问题
function performAsyncOperation(delay: number, message: string): Promisestring {return new Promise((resolve) {setTimeout(() {console.log(message);resolve(message);}, delay);});
}export async function issueAsyncAwait() {try {const delay 1000;let message Hello World;await performAsyncOperation(delay, message);console.log(Phase 01);await performAsyncOperation(delay, message);console.log(End The Callback);} catch (error) {console.log(Error:, error);}
}在这种情况下,我们想执行 console.log(‘Phase 1’) 但是performAsyncOperation 方法在一个单独的进程中执行我们的打印应该是在performAsyncOperation 方法执行前完成对吗? 经过检查我们可以看到这并不是我们所期待的。怎么回事?
顾名思义它等待整个代码块直到异步操作返回响应。因此这使得我们的代码同步并创建了一个瀑布调用模式,在这里我们的代码将一个接一个地调用。
因此如果我们的事件并不相互依赖如果我们的事件不依赖于非同步操作的输出我们不必一定要等到非同步操作完成对吗?
所以在这种情况下, 考虑使用回调 :
function performAsyncOperation(delay: number, message: string): Promisestring {return new Promise((resolve) {setTimeout(() {console.log(message);resolve(message);}, delay);});
}export async function asyncAwaitFix() {try {const delay 1000;let message Hello World;performAsyncOperation(delay, message).then((resp) console.log(Process the resp: ${resp}.));console.log(Phase 01);await performAsyncOperation(delay, message);console.log(End The Callback);} catch (error) {console.log(Error:, error);}
}如你所见我们重构了performAsyncOperation 方法并使用 .then() 回调。这样做可以让回调作为一个真正的回调执行并且不会在代码中创建任何等待。为了验证我们的理论,让我们检查一下输出: 如你所见Phase 01 首先打印了不再等待到 async 操作完成。
但是要小心使用这个因为我们可能会创建回调地狱!
循环的性能问题
接下来,让我们谈谈循环。我们都用 JavaScript 写过循环 for (let i 0; i 5; i) {console.log(Iteration number:, i);}我们循环了一组元素并对其进行了一些计算。但是如果我们必须在这里执行异步操作呢假设我们得到了一堆用户身份证。并被要求获取所有身份证的信息(注意我们的API不支持批量)。 我们可能会这样写
function getUserInfo(id: number) {return new Promise((resolve) {// 模拟异步setTimeout(() {resolve({ userId: id, name: User_${id}, email: user${id}example.com });}, 1000);});
}export async function asyncForLoopIssue(userIds: number[] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) {const usersInfo: any[] [];for (let i 0; i userIds.length; i) {const userInfo await getUserInfo(userIds[i]);usersInfo.push(userInfo);}console.log({ usersInfo });return usersInfo;
}现在这个代码再次没有问题。它将按预期在生产中发挥作用。但是我们被限制在这里的同步循环。这意味着一旦收集到单个用户信息我们的循环的下一次迭代将开始。因此这个函数将在10s后执行并像这样的同步输出 这是一个接一个发生的。
但我们该怎么解决?
可以用非线性来执行这个循环
function getUserInfo(id: number) {return new Promise((resolve) {setTimeout(() {resolve({ userId: id, name: User_${id}, email: user${id}example.com });}, 1000);});
}export async function loopAsyncFix(userIds: number[] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) {const promises userIds.map(async (id) {const userInfo await getUserInfo(id);return userInfo;})const usersInfo await Promise.all(promises);console.log({ usersInfo });return usersInfo;
}现在这种方法将产生相同的响应。然而它的实现方式有点不同。
在方法01中每次迭代都在当前的async操作完成后开始。 async 意味着它应该在不干扰主线程的情况下执行。
第二种方法坚持真正的异步方法因为它返回最终将执行的 Promise 对象。因此虽然我们是顺序运行它但是它将返回随机调用每个调用都是独立的并且在没有相关顺序的情况下按自己的速度执行。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/912077.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!