在 JavaScript 中使用回调函数的替代方案?

Alternative to using callback function in JavaScript?

我试图在第一个函数的滚动动画结束后调用第二个函数。我已经看到在类似情况下使用回调函数,但它们似乎更麻烦且难以阅读。有没有更简单或更干净的方法来达到相同的结果?

$('#nav_experience').click(function scrollToExperience(){
    $('html, body').animate({
        scrollTop: $('#Experience').offset().top
    }, 500);
})

function navbarExperienceActive(){
    scrollToExperience(function(){
        $('#nav_profile').removeClass('active');
        $('#nav_education').removeClass('active');
        $('#nav_contact').removeClass('active');
        $('#nav_experience').addClass('active');
    });
}

我也很欣赏第二个函数非常重复,可能有更优雅的方式来编写它,我对 JavaScript 比较陌生。

我们过去使用回调的情况的一种现代方法是使用 promises,当与 async 函数和 await 结合使用时特别有效. (从概念上讲,“promises”有很多名称——promises、futures、deferreds……)

Promise 本身仍然使用回调,但通过提供一种标准化形式来传递未来回调的 promises,它们显着改进了过去将回调作为参数传递的代码。

您的 scrollToExperience 可能看起来像这样,例如:

function scrollToExperience(){
    return new Promise(resolve => {
        $('html, body').animate({
            scrollTop: $('#Experience').offset().top
        }, 500, resolve);
    });
}

navbarExperienceActive 可能会这样使用它:

function navbarExperienceActive(){
    scrollToExperience().then(function(){
        $('#nav_profile').removeClass('active');
        $('#nav_education').removeClass('active');
        $('#nav_contact').removeClass('active');
        $('#nav_experience').addClass('active');
    });
}

从表面上看,变化不大;您仍然必须传递回调。但是,如果 navbarExperienceActive 必须将其与其他异步操作结合起来,以其他方式将其链接起来,等等,使用 promises 会使代码库之间的操作更简单、更标准。

但是如果 navbarExperienceActive 是一个 async 函数,它可以使用 await:

async function navbarExperienceActive(){
    await scrollToExperience();
    $('#nav_profile').removeClass('active');
    $('#nav_education').removeClass('active');
    $('#nav_contact').removeClass('active');
    $('#nav_experience').addClass('active');
}

它仍然使用 promises,但代码是根据其简单的逻辑流程编写的,而不是担心必须等待 scrollToExperience 完成的事实。

一个async函数总是returns一个承诺;当你在 async 函数中使用 await 时,如果你传递的值是一个承诺 (hand-waves details about "thenables"),它returns 它的 promise 然后根据你正在 awaiting 的 promise 发生的事情来解决这个 promise(and/or 它后面的逻辑)。


我应该注意到上面 scrollToExperience 返回的承诺有点不寻常,因为它总是履行承诺,它从不拒绝它。使用其承诺的代码依赖于它从不拒绝其承诺的事实。

不过,一般来说,重要的是要确保要么处理 promise 拒绝,要么将 promise 链 传递给其他可以处理的东西。那是 async 函数让生活更简单的另一个地方:它们自动传播 promise 拒绝,这与同步函数自动传播异常的方式完全相同。