如果异步本地存储在子线程中进行了修改,为什么它会在父线程中更改?

Why does the Async Local Storage change in the parent thread if it is modified in a child thread?

提问人:BlueSialia 提问时间:9/22/2023 更新时间:9/23/2023 访问量:60

问:

我在 Node.js HTTP 服务器中使用异步本地存储,以便为每个请求创建一个唯一的存储。我的理解是,当我执行异步函数时,子线程(即异步函数内部)将具有与父线程相同的存储,但是当子线程对其进行任何修改时,更改不会传播到父线程。

但我用以下代码尝试了它:

const als = new AsyncLocalStorage<any>();
const wait = (seconds: number) =>
    new Promise(res => setTimeout(res, seconds * 1000));

als.run(new Map(), async () => {
    als.getStore().set('key', 1);
    console.log('outside', als.getStore().get('key'));
    foo();
    bar();
    await wait(2);
    console.log('outside', als.getStore().get('key'));
});

async function foo(): Promise<void> {
    return new Promise(async (resolve, _) => {
        console.log('inside foo', als.getStore().get('key'));
        await wait(1);
        als.getStore().set('key', 2);
        console.log('inside foo', als.getStore().get('key'));
        resolve();
    });
}

async function bar(): Promise<void> {
    return new Promise(async (resolve, _) => {
        console.log('inside bar', als.getStore().get('key'));
        await wait(1);
        als.getStore().set('key', 3);
        console.log('inside bar', als.getStore().get('key'));
        resolve();
    });
}

控制台显示:

outside 1
inside foo 1
inside bar 1
inside foo 2
inside bar 3
outside 3

我本来以为最后一行是.我对异步本地存储的理解有误吗?outside 1

如果是这样,是否有可能实现我的期望?

JavaScript 节点 .js 打字稿 异步

评论

0赞 Bergi 9/23/2023
"当孩子对它进行任何修改时,这些变化不会传播到父母“——这种理解从何而来?您只创建一个在函数之间共享的单个函数。new Map()
0赞 Bergi 9/23/2023
顺便说一句,永远不要将异步函数作为执行器传递给新的 Promise
0赞 BlueSialia 9/23/2023
@Bergi - 我确信我过去读过关于异步本地存储的多种解释,声称有这样的事情。但现在我找不到了。我很困惑。现在我不知道为什么我相信这一点。当然还有那些奇怪的功能。我用我想到的第一件事写了 foo 和 bar。

答:

1赞 Bergi 9/23/2023 #1

您需要区分上下文和可变存储。当子项使用新值进入新上下文时,这确实不会影响父执行的上下文。但在您的示例中,您只输入一个上下文,并在其中存储一个在父级和子级之间共享的上下文。通过设置键值对来修改该映射会修改也与父级共享的单个实例。new Map()Map

您可能根本不想使用 a。相反,请使用 or 创建新上下文,并将计数器直接存储为上下文值:MapenterWithrun

const als = new AsyncLocalStorage<any>();
const wait = (seconds: number) =>
    new Promise(res => setTimeout(res, seconds * 1000));

als.run(1, async () => {
    console.log('outside', als.getStore());
    foo();
    bar();
    await wait(2);
    console.log('outside', als.getStore()); // still 1!
});

async function foo(): Promise<void> {
    console.log('inside foo', als.getStore()); // 1
    await wait(1);
    als.enterWith(2);
    console.log('inside foo', als.getStore()); // now 2
}

async function bar(): Promise<void> {
    console.log('inside bar', als.getStore()); // 1
    als.run(3, async () => {
        await wait(1);
        console.log('inside bar', als.getStore()); // 3 in here
    });
}

你会想要 ,因为如果你把它放在第一个函数调用之前,就会影响后续的函数调用(尝试交换 and 语句,看看会发生什么)。run()enterWith()awaitasync functionawait wait(1);als.enterWith(2);foo