考虑以下代码:

async function test(label) {
    console.log(`BEFORE: ${label}`);
    await waitForSomething(label);
    console.log(`AFTER: ${label}`);
}

function waitForSomething(label) {
    return {
        then(onFulfilled) {
            console.log(`FULFILL: ${label}`);
            onFulfilled();
        }
    }
}

console.log('START');
test('A');
test('B');
console.log('END');

它给出以下输出:

START
BEFORE: A
BEFORE: B
END
FULFILL: A
FULFILL: B
AFTER: A
AFTER: B

我希望以同步方式在FULFILL A之后立即触发AFTER A,这将给出:

START
BEFORE: A
BEFORE: B
END
FULFILL: A
AFTER: A
FULFILL: B
AFTER: B

其 idea 是,我正在编写一个具有全局状态的库,我希望在用户编写的一系列同步调用中保持一致.由于多个链可能同时运行,因此我需要在呼叫链"恢复"之前运行一些代码.我知道乍一看这可能是一个奇怪或坏主意,但这是一个单独的问题.有办法做到这一点吗?

我想过用类似unwrap方法的方法返回一个对象,该方法将触发代码,例如:

(await waitForSomething(label)).unwrap();

但这迫使用户在所有相关通话中使用unwrap(),这很乏味.此外,他们可能会忘记这样做,并在不理解原因的情况下遇到意想不到的问题.

编辑:我将进一步阐述这背后的动机.基本上,这个 idea 是为了避免必须将状态参数一路向下传递到链并使其作为全局变量访问.

而不是做:

async function test(state) {
    let input = await waitForUserInput();
    doSomethingWith(state, input);
}

function doSomethingWith(state, input) {
    // ...
}

我可以做:

async function test() {
    let input = await waitForUserInput();
    doSomethingWith(input);
}

function doSomethingWith(input) {
    let state = MyLib.state;
    // ...
}

这可以避免必须在test触发的每个函数调用中携带state.然而,由于其中一些函数是Deliverc,并且由于其中多个可以同时运行,并且由于每个根函数有不同的状态(例如test),因此当Deliverc函数返回时,我需要将MyLib.state设置为正确的值.同样,这是在库的上下文中:库为test函数提供状态并运行它,但test本身是由用户编写的.

编辑2:正如@user3840170所指出的那样,我需要的是https://github.com/tc39/proposal-async-context/,这只是提案的第二阶段.所以我想这目前是不可能的.

推荐答案

我想目标不是按照您想要的顺序创建日志(log),而是保留临时呼叫链中的状态.您可以在await之后对恢复状态的微任务进行排队.我想需要一个更详细的解决方案来满足您的所有需求:

async function test(label) {
    console.log(`BEFORE: ${state}`);
    await waitForSomething();
    console.log(`AFTER: ${label} => ${state}`);
    await waitForSomething();
    console.log(`AFTER 2: ${label} => ${state}`);
}

function waitForSomething() {
    let cached = state;
    return {
        then(onFulfilled) {
            setTimeout(() => {
              console.log(`FULFILL: ${cached}`);
              // call this before the callback, so the code after `await` would receive the proper state
              queueMicrotask(() => state = cached);
              onFulfilled();
            }, Math.random()*1000);
        }
    }
}

console.log('START');
let state = 'A'; // your MyLib.state 
test('A');
state = 'B';
test('B');
state = 'C';
test('C');
console.log('END');
.as-console-wrapper{max-height:100% !important}

Javascript相关问答推荐

如何使用JavaScript对切换的声音设置音量

二维数组,过滤并返回带有索引而不是值的新数组

在Phaser中查找哪个物体处于碰撞中

如何在ReactJS中修复这种不需要的按钮行为?

基于每个索引迭代嵌套对象

无法将nPM simplex-noise包导入在JS项目中工作

获取加载失败:获取[.]添加时try 将文档添加到Firerestore,Nuxt 3

为什么我的includes声明需要整个字符串?

如何将连续的十六进制字符串拆分为以空间分隔的十六进制块,每个十六进制块包含32个二元组?

Cookie中未保存会话数据

Snowflake JavaScript存储过程返回成功,尽管预期失败

如何在Javascript中使用Go和检索本地托管Apache Arrow Flight服务器?

函数返回与输入对象具有相同键的对象

屏幕右侧屏障上的产卵点""

如何调用名称在字符串中的实例方法?

如果Arrow函数返回函数,而不是为useEffect返回NULL,则会出现错误

IF语句的计算结果与实际情况相反

当用户点击保存按钮时,如何实现任务的更改?

基于产品ID更新条带产品图像的JavaScript命中错误

为什么我的SoupRequest";被重置为初始值,以及如何修复它?