异步 JS 中的嵌套回调

Nested callbacks in asynchronous JS

提问人:kbl 提问时间:10/10/2023 最后编辑:Barmarkbl 更新时间:10/10/2023 访问量:46

问:

我刚开始学习 Javascript 的异步特性,遇到了其他人所说的回调地狱。有人可以就以下处理嵌套回调的方法提供一些想法吗?这种方法是否可能存在问题或缺点。

比方说,我想异步随机生成一个数字。然后,通过函数修改生成的数字。

function delayedFunctionalites(wait, ...funcs) {
  setTimeout(() => {
    let randNum = Math.floor(Math.random() * 10);
    console.log(`randNum is ${randNum}`);

    let updatedRandNum = funcs.reduce((acc, func) => func(acc), randNum);

    console.log(`updatedRandNum is ${updatedRandNum}`);
  }, wait);
}

const addOne = (input) => input + 1;
const multBy2 = (input) => input * 2;
const addFive = (input) => input + 5;

delayedFunctionalites(2000, addOne, multBy2, addFive);

// Output: randNum is 8
// Output: updatedRandNum is 23

嵌套 的 JavaScript 回调

评论

1赞 evolutionxbox 10/10/2023
你想要什么样的想法?提供的代码似乎没有糟糕的嵌套回调?
3赞 Barmar 10/10/2023
您没有嵌套的回调。你只有一个异步回调,它会同步调用所有函数。
0赞 kbl 10/10/2023
你们是对的。我对这个问题的心理模型是错误的。
0赞 VLAZ 10/10/2023
已经有了一个回调地狱的解决方案,它的承诺。
0赞 VLAZ 10/10/2023
也是功能组成。funcs.reduce((acc, func) => func(acc), randNum);

答:

1赞 nullromo 10/10/2023 #1

我认为您从评论中得到了一些很好的见解,但为了完整起见,这里有一些不同的方法可以进行承诺链接。

测试功能

此函数在延迟 1 秒后将数字加 4。这在下面的示例中使用。

const testFunction = async (input: number) => {
    return new Promise<number>((resolve) => {
        setTimeout(() => {
            resolve(input + 4);
        }, 1000);
    });
};

一遍又一遍地打电话.then()

这是您通常将承诺链接在一起的方式。每一个都紧随其后,并使用前一个的结果。

const callbackChaining = async () => {
    const value = 5;
    console.log('Value is', value);
    const result = await testFunction(value)
        .then(async (result) => {
            console.log('Value is', result);
            return testFunction(result);
        })
        .then(async (result) => {
            console.log('Value is', result);
            return testFunction(result);
        })
        .then(async (result) => {
            console.log('Value is', result);
            return testFunction(result);
        });
    console.log('Result is', result);
};

将循环与forawait

您可以使用普通循环来获取每个操作的结果,然后对其调用下一个操作。await

请注意,有一个 eslint 规则警告不要在循环中使用 await,因此您需要研究一下它是否与您的情况相关。

const forLoop = async () => {
    let value = 5;
    const operations = [testFunction, testFunction, testFunction, testFunction];
    for (const operation of operations) {
        console.log('Value is', value);
        // eslint-disable-next-line no-await-in-loop
        value = await operation(value);
    }
    console.log('Result is', value);
};

Array.prototype.reduce()

您在问题中使用了。这里有一个类似的使用方法,可以将承诺列表的结果链接在一起。reduce

const arrayReduce = async () => {
    const value = 5;
    const operations = [testFunction, testFunction, testFunction, testFunction];
    const result = await operations.reduce(async (result, operation) => {
        return result.then(async (value) => {
            console.log('Value is', value);
            return operation(value);
        });
    }, Promise.resolve(value));
    console.log('Result is', result);
};