當您聽到「非同步本地儲存」這個短語時,您會想到什麼?您最初可能認為它指的是基於瀏覽器的本地儲存的某種神奇實現。然而,這個假設是不正確的。非同步本地儲存既不與瀏覽器相關,也不是典型的儲存機制。您可能使用過的一兩個庫在幕後使用它。在很多情況下,這個功能可以讓你免於處理混亂的程式碼。
什麼是異步本地儲存?
非同步本地儲存是 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編輯器

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

Dreamweaver Mac版
視覺化網頁開發工具

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。