Penyebaran Konteks dengan AsyncLocalStorage
Bawa keadaan berskopkan permintaan merentas sempadan asinkron tanpa meneruskan prop menggunakan AsyncLocalStorage.
Penyebaran Konteks dengan AsyncLocalStorage ialah pelajaran Kem Intensif Pembangunan Backend Node.js percuma di CoddyKit. Ini ialah pelajaran 4 daripada 4. Anda boleh membaca keseluruhan pelajaran di bawah secara percuma — kemudian berlatih secara praktikal dalam pelayar menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Kem Intensif Pembangunan Backend Node.js, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Kem Intensif Pembangunan Backend Node.js merangkumi sejumlah 4 pelajaran.
Masalah Penghantaran Prop
Dalam perkhidmatan bahagian belakang, data berskop permintaan seperti ID permintaan, pengguna yang disahkan atau ID penyewa diperlukan jauh di dalam timbunan panggilan anda: dalam repositori, pengelog dan client HTTP keluar.
Penyelesaian naif ialah penghantaran prop — menghantar argumen ctx melalui setiap fungsi:
- Setiap tandatangan fungsi tercemar dengan parameter
ctx. - Satu penyerahan yang terlepas menyebabkan panggilan hiliran kehilangan konteks.
- Kod pustaka yang bukan milik anda langsung tidak boleh menerima
ctxanda.
Kita memerlukan cara untuk membawa keadaan bagi setiap permintaan secara tersirat, merentasi setiap await dan panggilan balik. Itulah yang disediakan oleh AsyncLocalStorage.
async function handler(ctx, req) {
const user = await loadUser(ctx, req.userId);
return await renderPage(ctx, user);
}
async function loadUser(ctx, id) {
log(ctx, 'loading user'); // ctx passed again
return await db.find(ctx, id); // and again...
}
// Every layer must accept and forward ctx by hand.Apakah AsyncLocalStorage
AsyncLocalStorage berada dalam modul terbina dalam node:async_hooks. Anggaplah ia sebagai penyimpanan setempat utas untuk dunia tak segerak: stor yang kekal terikat pada rantaian logik operasi tak segerak.
Anda memanggil als.run(store, callback) untuk menetapkan stor, kemudian di mana-mana sahaja dalam panggilan balik itu — tanpa mengira berapa banyak await, rantaian Promise, setTimeout atau pemancar peristiwa yang bersarang — als.getStore() mengembalikan stor yang sama.
- Setiap permintaan serentak mendapat stor terpencilnya sendiri.
- Tiada global, tiada keadaan perlumbaan antara permintaan.
- Dikuasakan oleh penjejakan konteks tak segerak Node di sebalik tabir.
import { AsyncLocalStorage } from 'node:async_hooks';
const als = new AsyncLocalStorage();
als.run({ requestId: 'abc-123' }, () => {
setTimeout(() => {
const store = als.getStore();
console.log(store.requestId); // 'abc-123'
}, 10);
});run() Menetapkan Konteks
API teras ialah als.run(store, fn, ...args). Ia melaksanakan fn secara segerak, tetapi mengikat store pada keseluruhan pepohon tak segerak yang terhasil daripadanya.
storeboleh berupa apa-apa nilai — selalunya objek biasa atauMap.run()mengembalikan apa sahaja yang dikembalikan olehfn(termasuk Promise).- Di luar panggilan balik,
getStore()mengembalikanundefined.
Petikan ini menunjukkan bahawa stor kekal merentasi await pada pemasa sebenar.
import { AsyncLocalStorage } from 'node:async_hooks';
import { setTimeout as sleep } from 'node:timers/promises';
const als = new AsyncLocalStorage();
async function deep() {
await sleep(5);
return als.getStore()?.requestId;
}
await als.run({ requestId: 'r-42' }, async () => {
const id = await deep();
console.log('inside run:', id); // 'r-42'
});
console.log('outside run:', als.getStore()); // undefinedMenyepadukannya ke dalam Pelayan HTTP
Corak dalam pelayan sebenar: balut kerja bagi setiap permintaan dalam als.run() di bahagian paling luar, dan isikan stor dengan ID permintaan baharu. Semua bahagian hiliran kemudiannya boleh membacanya.
Contoh di bawah hanya menggunakan modul terbina dalam node:http, jadi tiada kebergantungan rangka kerja. Setiap permintaan masuk mendapat stor sendiri yang terpencil daripada permintaan serentak.
import http from 'node:http';
import { randomUUID } from 'node:crypto';
import { AsyncLocalStorage } from 'node:async_hooks';
const als = new AsyncLocalStorage();
function currentRequestId() {
return als.getStore()?.requestId ?? 'no-context';
}
const server = http.createServer((req, res) => {
als.run({ requestId: randomUUID() }, async () => {
// deep call needs no ctx argument
await Promise.resolve();
res.end('request id: ' + currentRequestId());
});
});
server.listen(3000, () => console.log('listening on 3000'));Corak Middleware Express
Dalam Express, tempat idiomatik untuk memanggil als.run() ialah middleware yang dipasang terlebih dahulu. Middleware itu mesti memanggil next() di dalam panggilan balik supaya seluruh rantaian seterusnya mewarisi stor tersebut.
- Baca pengepala
x-request-idyang masuk jika proksi atau perkhidmatan huluan membekalkannya; jika tiada, jana UUID baharu. - Setiap pengendali, perkhidmatan dan pengelog selepas itu boleh membaca stor tanpa menerimanya sebagai argumen.
import { AsyncLocalStorage } from 'node:async_hooks';
import { randomUUID } from 'node:crypto';
export const als = new AsyncLocalStorage();
export function contextMiddleware(req, res, next) {
const store = {
requestId: req.headers['x-request-id'] || randomUUID(),
startedAt: Date.now(),
};
als.run(store, () => next()); // next() runs inside the context
}
export function getStore() {
const store = als.getStore();
if (!store) throw new Error('No request context');
return store;
}Pelogan Berstruktur Berasaskan Konteks
Faedah terbesar: pencatat yang mengecap setiap baris secara automatik dengan ID permintaan — tiada pemanggil perlu menghantarnya.
Pencatat membaca store itu sendiri. Jika tiada konteks aktif (contohnya, kod permulaan), ia berfungsi secara teratur dengan keupayaan yang dikurangkan.
import { AsyncLocalStorage } from 'node:async_hooks';
const als = new AsyncLocalStorage();
function log(level, msg, extra = {}) {
const store = als.getStore();
const line = {
ts: new Date().toISOString(),
level,
msg,
requestId: store?.requestId ?? null,
...extra,
};
console.log(JSON.stringify(line));
}
als.run({ requestId: 'req-7' }, () => {
log('info', 'user fetched', { userId: 99 });
});
log('warn', 'no request context here');Mengubah Store Semasa Permintaan
Oleh sebab store ialah rujukan (objek atau Map), anda boleh memperkayakannya selepas pengesahan selesai. Baris log seterusnya akan mengambil medan baharu secara automatik.
- Mulakan dengan data minimum di pinggir (ID permintaan).
- Selepas perisian tengah pengesahan dijalankan, lampirkan
userIddantenantIdpada objek store yang sama. - Utamakan
Mapjika anda mahukan API berasaskan kunci yang jelas; objek biasa juga memadai dan sedikit lebih pantas.
import { AsyncLocalStorage } from 'node:async_hooks';
const als = new AsyncLocalStorage();
function set(key, value) {
const store = als.getStore();
if (store) store.set(key, value);
}
function get(key) {
return als.getStore()?.get(key);
}
als.run(new Map([['requestId', 'r-1']]), () => {
// ... later, after authenticating:
set('userId', 42);
set('tenantId', 'acme');
console.log(get('requestId'), get('userId'), get('tenantId'));
});enterWith berbanding run
Terdapat dua cara untuk menetapkan store:
als.run(store, fn)— mengehadkan store kepadafndan keturunan tak segeraknya. Apabilafnselesai, konteks itu hilang. Utamakan cara ini.als.enterWith(store)— menetapkan store untuk pelaksanaan segerak semasa dan segala-galanya selepasnya dalam sumber tak segerak yang sama, tanpa keluar secara automatik.
enterWith perlu digunakan dengan berhati-hati: jika anda memanggilnya dalam sumber tak segerak yang berjangka hayat panjang, ia boleh terbocor ke kerja kemudian yang tidak berkaitan. Gunakan run melainkan anda mempunyai sebab khusus (contohnya, anda tidak boleh membalut panggilan balik).
import { AsyncLocalStorage } from 'node:async_hooks';
const als = new AsyncLocalStorage();
// Scoped and safe — context ends with the callback:
als.run({ id: 'A' }, () => {
console.log(als.getStore().id); // 'A'
});
console.log(als.getStore()); // undefined
// enterWith persists with no clear boundary — easy to leak:
als.enterWith({ id: 'B' });
console.log(als.getStore().id); // 'B' (and stays set!)Tempat Konteks Boleh Terputus
AsyncLocalStorage mengikuti janji natif, async/await, pemasa dan kebanyakan pemancar peristiwa. Namun, konteks boleh hilang dalam beberapa keadaan:
- Kerja yang dijadualkan sebelum
run()— contohnya, kumpulan sambungan atau baris gilir yang dicipta semasa permulaan menjalankan panggilan baliknya di luar mana-mana store permintaan. - Sesetengah pustaka lama yang mengumpulkan atau menggunakan semula sumber merentas permintaan boleh membawa store yang lapuk.
- Panggilan balik yang ditanggalkan secara manual, disimpan dalam tatasusunan global dan dipanggil kemudian.
Pembaikan ketika mengintegrasikan kod sedemikian ialah als.bind(fn) (atau AsyncResource.bind), yang mengambil gambaran konteks semasa dan menggunakannya semula setiap kali fungsi itu dipanggil kemudian.
import { AsyncLocalStorage } from 'node:async_hooks';
const als = new AsyncLocalStorage();
const queue = [];
als.run({ requestId: 'r-9' }, () => {
// bind captures the current store for later execution
queue.push(als.bind(() => {
console.log('later:', als.getStore()?.requestId);
}));
});
// Runs outside the run() callback, but context is preserved:
queue.forEach((fn) => fn()); // later: r-9Modul Konteks Boleh Guna Semula
Dalam amalan, anda memusatkan store dalam satu modul kecil supaya bahagian kod yang lain hanya mengimport pembantu — tanpa menyentuh tika AsyncLocalStorage secara langsung.
Ini memastikan API kekal kemas: runWithContext() di pinggir, requestId() / getUser() di tempat lain.
import { AsyncLocalStorage } from 'node:async_hooks';
import { randomUUID } from 'node:crypto';
const als = new AsyncLocalStorage();
export function runWithContext(seed, fn) {
const store = { requestId: randomUUID(), ...seed };
return als.run(store, fn);
}
export function context() {
return als.getStore() ?? null;
}
export function requestId() {
return context()?.requestId ?? null;
}
export function setUser(user) {
const store = als.getStore();
if (store) store.user = user;
}Prestasi dan Amalan Baik
AsyncLocalStorage dalam Node moden (v16+) disokong oleh pelaksanaan natif yang cekap dan bebannya kecil — boleh diterima untuk hampir semua beban kerja web. Namun begitu, ikuti beberapa amalan baik:
- Gunakan satu tika
AsyncLocalStorageyang berjangka hayat panjang bagi setiap keperluan, bukan satu bagi setiap permintaan. - Pastikan store kecil; ia ialah keadaan mengikut skop permintaan, bukan cache.
- Jangan simpan rahsia yang anda tidak mahu muncul dalam log yang membaca store.
- Utamakan
run()berbandingenterWith()untuk mendapatkan pembersihan automatik. - Sentiasa kendalikan keadaan store
undefinedbagi kod yang mungkin dijalankan di luar permintaan.
Semakan Pantas
Uji pemahaman anda tentang cara mewujudkan konteks permintaan dengan selamat.
Imbas Kembali
Anda telah mempelajari cara menyebarkan keadaan mengikut skop permintaan merentas sempadan tak segerak tanpa meneruskan parameter melalui setiap lapisan:
AsyncLocalStoragedaripadanode:async_hooksialah storan setempat bebenang untuk dunia tak segerak.als.run(store, fn)mengikat store kepadafndan semua keturunan tak segeraknya;als.getStore()membacanya di mana-mana; di luar pelaksanaan itu, nilainya ialahundefined.- Sambungkannya sekali di pinggir (pelayan HTTP atau perisian tengah pertama), mulakan dengan ID permintaan, kemudian perkayakan store selepas pengesahan.
- Pencatat yang peka konteks boleh mengecap setiap baris dengan ID permintaan secara automatik.
- Utamakan
run()berbandingenterWith()untuk pembersihan automatik, dan gunakanals.bind()/AsyncResource.bind()untuk mengekalkan konteks merentas panggilan balik yang ditanggalkan. - Pastikan store kecil, kendalikan keadaan tanpa konteks dan pusatkan capaian dalam satu modul.
Pelajari JavaScript dengan tutor kecerdasan buatan — percuma
Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.
- Kursus
- 22
- Pelajaran
- 92
Soalan Lazim
Adakah pelajaran “Penyebaran Konteks dengan AsyncLocalStorage” percuma?
Ya — teks penuh “Penyebaran Konteks dengan AsyncLocalStorage” boleh dibaca secara percuma di web ini. Untuk berlatih secara interaktif menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7, serta membuka kunci baki kursus Kem Intensif Pembangunan Backend Node.js, tingkat taraf kepada CoddyKit PRO. Kursus Kem Intensif Pembangunan Backend Node.js merangkumi sejumlah 4 pelajaran.
Apakah yang akan saya pelajari dalam “Penyebaran Konteks dengan AsyncLocalStorage”?
Bawa keadaan berskopkan permintaan merentas sempadan asinkron tanpa meneruskan prop menggunakan AsyncLocalStorage. Anda berlatih Kem Intensif Pembangunan Backend Node.js menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.
Adakah saya memerlukan pengalaman untuk memulakan Kem Intensif Pembangunan Backend Node.js?
Tiada pengalaman terdahulu diperlukan. Pembelajaran Kem Intensif Pembangunan Backend Node.js di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 4 daripada 4.
Berapa lamakah pelajaran “Penyebaran Konteks dengan AsyncLocalStorage” diambil?
Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.
Bolehkah saya menulis dan menjalankan kod dalam pelajaran Kem Intensif Pembangunan Backend Node.js ini?
Ya. Setiap pelajaran Kem Intensif Pembangunan Backend Node.js menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.
Semua pelajaran dalam kursus ini
- Logging Berstruktur dengan ID Korelasi
- Penjejakan Teragih dengan Rentang OpenTelemetry
- Mendedahkan Metrik Aplikasi dan Kaedah RED
- Penyebaran Konteks dengan AsyncLocalStorage