当您听到“异步本地存储”这个短语时,您会想到什么?您最初可能认为它指的是基于浏览器的本地存储的某种神奇实现。然而,这个假设是不正确的。异步本地存储既不与浏览器相关,也不是典型的存储机制。您可能使用过的一两个库在幕后使用它。在很多情况下,这个功能可以让你免于处理混乱的代码。
什么是异步本地存储?
异步本地存储是 Node.js 中引入的功能,最初在 v13.10.0 和 v12.17.0 版本中添加,后来在 v16.4.0 中稳定下来。它是 async_hooks 模块的一部分,该模块提供了一种跟踪 Node.js 应用程序中的异步资源的方法。该功能允许创建多个异步函数可以访问的共享上下文,而无需显式传递它。上下文在传递给 AsyncLocalStorage 实例的 run() 方法的回调中执行的每个(也是唯一)操作中可用。
使用 AsyncLocalStorage 的模式
在深入示例之前,让我们先解释一下我们将使用的模式。
初始化
import { AsyncLocalStorage } from "async_hooks"; import { Context } from "./types"; export const asyncLocalStorage = new AsyncLocalStorage<context>(); // export const authAsyncLocalStorage = new AuthAsyncLocalStorage<authcontext>() </authcontext></context>
在上面的模块中,我们初始化 AsyncLocalStorage 的实例并将其导出为变量。
用法
asyncLocalStorage.run({ userId }, async () => { const usersData: UserData = await collectUsersData(); console.log("usersData", usersData); }); // (method) AsyncLocalStorage<unknown>.run<promise>>(store: unknown, callback: () => Promise<void>): Promise<void> (+1 overload) </void></void></promise></unknown>
run() 方法有两个参数:存储(包含我们想要共享的数据)和回调(我们放置逻辑的位置)。因此,回调中的每个函数调用都可以访问存储,从而允许跨异步操作无缝共享数据。
async function collectUsersData() { const context = asyncLocalStorage.getStore(); }
要访问上下文,我们导入实例并调用 asyncLocalStorage.getStore() 方法。最棒的是,从 getStore() 检索的存储是类型化的,因为我们在初始化期间将 Context 类型传递给了 AsyncLocalStorage: new AsyncLocalStorage
异步本地存储作为身份验证上下文
没有身份验证系统的 Web 应用程序。我们必须验证身份验证令牌并提取用户信息。一旦我们获得了用户身份,我们希望使其在路由处理程序中可用,并避免在每个处理程序中重复代码。让我们看看如何利用 AsyncLocalStorage 来实现身份验证上下文,同时保持代码整洁。
我在这个例子中选择了 fastify。
根据文档 fastify 是:
快速且低开销的 Web 框架,适用于 Node.js
好的,让我们开始吧:
- 安装 fastify:
import { AsyncLocalStorage } from "async_hooks"; import { Context } from "./types"; export const asyncLocalStorage = new AsyncLocalStorage<context>(); // export const authAsyncLocalStorage = new AuthAsyncLocalStorage<authcontext>() </authcontext></context>
- 定义我们的身份验证上下文的类型:
asyncLocalStorage.run({ userId }, async () => { const usersData: UserData = await collectUsersData(); console.log("usersData", usersData); }); // (method) AsyncLocalStorage<unknown>.run<promise>>(store: unknown, callback: () => Promise<void>): Promise<void> (+1 overload) </void></void></promise></unknown>
- 初始化 AsyncLocalStorage 的实例,将其分配给变量,然后导出该变量。请记住传递相关类型:new AsyncLocalStorage
()。
async function collectUsersData() { const context = asyncLocalStorage.getStore(); }
- 初始化 Fastify 实例并添加用于错误处理的实用程序:
npm install fastify
现在到了非常重要的部分。我们将添加一个 onRequest 钩子来使用 authAsyncLocalStorage.run() 方法包装处理程序。
type Context = Map;
成功验证后,我们从 authAsyncLocalStorage 调用 run() 方法。作为存储参数,我们传递身份验证上下文以及从令牌中检索到的 userId。在回调中,我们调用done函数来继续Fastify生命周期。
如果我们有需要异步操作的身份验证检查,我们应该将它们添加到回调中。这是因为,根据文档:
使用 async/await 或返回 Promise 时,done 回调不可用。如果您在这种情况下调用完成回调,则可能会发生意外行为,例如处理程序的重复调用
这是一个示例:
import { AsyncLocalStorage } from "async_hooks"; import { Context } from "./types"; export const authAsyncLocalStorage = new AsyncLocalStorage<context>(); </context>
我们的示例只有一条受保护的路线。在更复杂的场景中,您可能需要仅使用身份验证上下文包装特定路由。在这种情况下,您可以:
- 将 onRequest 挂钩包装在仅应用于特定路由的自定义插件中。
- 在 onRequest 钩子本身内添加路由区分逻辑。
好吧,我们的上下文已经设置完毕,我们现在可以定义一条受保护的路由:
import Fastify from "fastify"; /* other code... */ const app = Fastify(); function sendUnauthorized(reply: FastifyReply, message: string) { reply.code(401).send({ error: `Unauthorized: ${message}` }); } /* other code... */
代码非常简单。我们导入 authAsyncLocalStorage,检索 userId,初始化 UserRepository 并获取数据。这种方法使路由处理程序保持干净和专注。
探索 Next.js 如何使用异步本地存储
在此示例中,我们将重新实现 Next.js 中的 cookies 助手。但是等等,这是一篇关于 AsyncLocalStorage 的文章,对吗?那么我们为什么要谈论cookie呢?答案很简单:Next.js 使用 AsyncLocalStorage 来管理服务器上的 cookie。这就是为什么在服务器组件中读取 cookie 如此简单:
import Fastify from "fastify"; import { authAsyncLocalStorage } from "./context"; import { getUserIdFromToken, validateToken } from "./utils"; /* other code... */ app.addHook( "onRequest", (request: FastifyRequest, reply: FastifyReply, done: () => void) => { const accessToken = request.headers.authorization?.split(" ")[1]; const isTokenValid = validateToken(accessToken); if (!isTokenValid) { sendUnauthorized(reply, "Access token is invalid"); } const userId = accessToken ? getUserIdFromToken(accessToken) : null; if (!userId) { sendUnauthorized(reply, "Invalid or expired token"); } authAsyncLocalStorage.run(new Map([["userId", userId]]), async () => { await new Promise((resolve) => setTimeout(resolve, 2000)); sendUnauthorized(reply, "Invalid or expired token"); done(); }); }, ); /* other code... */
我们使用next/headers导出的cookies函数,它提供了多种管理cookies的方法。但这在技术上怎么可能呢?
现在是时候开始我们的重新实施了”
首先,我想提一下,这个示例是基于我从 Lee Robinson 的精彩视频以及深入研究 Next.js 存储库中获得的知识。
在此示例中,我们将使用 Hono 作为我们的服务器框架。我选择它有两个原因:
- 我只是想尝试一下。
- 它为 JSX 提供了坚实的支持。
首先安装Hono:
import { AsyncLocalStorage } from "async_hooks"; import { Context } from "./types"; export const asyncLocalStorage = new AsyncLocalStorage<context>(); // export const authAsyncLocalStorage = new AuthAsyncLocalStorage<authcontext>() </authcontext></context>
现在,初始化Hono并添加中间件:
asyncLocalStorage.run({ userId }, async () => { const usersData: UserData = await collectUsersData(); console.log("usersData", usersData); }); // (method) AsyncLocalStorage<unknown>.run<promise>>(store: unknown, callback: () => Promise<void>): Promise<void> (+1 overload) </void></void></promise></unknown>
代码类似于 Fastify 示例中的中间件,不是吗?为了设置上下文,我们使用 setCookieContext,它是从 cookies 模块导入的 - 我们自定义的 cookies 函数的简单实现。让我们跟随 setCookieContext 函数并导航到导入它的模块:
async function collectUsersData() { const context = asyncLocalStorage.getStore(); }
setCookieContext 函数(其返回值我们传递给 Hono 中间件中的 cookieAsyncLocalStorage.run())从代表 hono 上下文的 c 参数中提取 cookie,并将它们与提供用于管理 cookie 的实用函数的闭包捆绑在一起。
我们的 cookie 功能复制了 next/headers 中 cookie 的功能。它利用 cookieAsyncLocalStorage.getStore() 方法来访问调用时传递给 cookieAsyncLocalStorage.run() 的相同上下文。
我们将 cookies 函数的返回包装在一个承诺中,以模仿 Next.js 实现的行为。在版本 15 之前,此函数是同步的。现在,在当前的 Next.js 代码中,cookie 返回的方法被附加到一个 Promise 对象,如以下简化示例所示:
npm install fastify
值得一提的另一点是,在我们的例子中,使用 cookies.setCookie 和 cookies.deleteCookie 总是会抛出错误,类似于在服务器组件中设置 cookie 时在 Next.js 中观察到的行为。我们硬编码了这个逻辑,因为在原来的实现中,我们是否可以使用setCookie或deleteCookie取决于存储在称为RequestStore的存储中的phase(WorkUnitPhase)属性(这是AsyncLocalStorage的实现,也存储cookie)。然而,这个主题更适合另一篇文章。为了使这个示例简单,我们省略 WorkUnitPhase 的模拟。
现在我们需要添加 React 代码。
- 添加App组件:
type Context = Map;
- 添加管理cookie的组件:
import { AsyncLocalStorage } from "async_hooks"; import { Context } from "./types"; export const authAsyncLocalStorage = new AsyncLocalStorage<context>(); </context>
cookie 的用法与 Next.js React 服务器组件中的用法类似。
- 添加路由处理程序来渲染模板:
import Fastify from "fastify"; /* other code... */ const app = Fastify(); function sendUnauthorized(reply: FastifyReply, message: string) { reply.code(401).send({ error: `Unauthorized: ${message}` }); } /* other code... */
我们的模板是通过 hono 上下文中的 html 方法渲染的。这里的关键点是路由处理程序在 asyncLocalStorage.run() 方法中运行,该方法采用 cookieContext。这样一来,我们就可以在 DisplayCookies 组件中通过 cookies 函数来访问这个上下文了。
不可能在React服务器组件中设置cookie,所以我们需要手动设置:
让我们刷新一下页面:
现在,我们的 cookie 已成功检索并显示。
结论
asyncLocalStorage 还有更多用例。此功能允许您在几乎任何服务器框架中构建自定义上下文。 asyncLocalStorage 上下文封装在 run() 方法的执行中,使其易于管理。它非常适合处理基于请求的场景。该 API 简单而灵活,通过为每个状态创建实例来实现可扩展性。可以无缝地维护身份验证、日志记录和功能标志等单独的上下文。
尽管它有很多好处,但仍有一些注意事项需要牢记。我听说 asyncLocalStorage 在代码中引入了太多“魔力”。我承认,当我第一次使用此功能时,我花了一些时间才完全掌握这个概念。另一件需要考虑的事情是将上下文导入模块会创建一个您需要管理的新依赖项。然而,最终,通过深度嵌套的函数调用传递值要糟糕得多。
感谢您的阅读,我们下一篇文章见!?
PS:您可以在这里找到示例(另加一个奖励)
博客帖子来源:https://www.aboutjs.dev/en/async-local-storage-is-here-to-help-you
以上是异步本地存储可以为您提供帮助的详细内容。更多信息请关注PHP中文网其他相关文章!

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

JavaScript在现实世界中的应用包括服务器端编程、移动应用开发和物联网控制:1.通过Node.js实现服务器端编程,适用于高并发请求处理。2.通过ReactNative进行移动应用开发,支持跨平台部署。3.通过Johnny-Five库用于物联网设备控制,适用于硬件交互。

我使用您的日常技术工具构建了功能性的多租户SaaS应用程序(一个Edtech应用程序),您可以做同样的事情。 首先,什么是多租户SaaS应用程序? 多租户SaaS应用程序可让您从唱歌中为多个客户提供服务


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

ZendStudio 13.5.1 Mac
功能强大的PHP集成开发环境

Dreamweaver Mac版
视觉化网页开发工具

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。