JavaScript承诺可以(应该)用于相互依赖的函数吗?

JavaScript承诺可以(应该)用于相互依赖的函数吗?,javascript,asynchronous,promise,Javascript,Asynchronous,Promise,承诺对于在异步调用的末尾附加某些内容非常有用,而异步调用不需要知道它的跟随者。可以在更一般或更复杂的情况下使用承诺,在异步调用中间需要回调,如果可以,那么它能提供相同级别的分离吗? 例如,假设您有两个函数a和b,其中执行顺序从a转移到b,再返回两次 使用回调: function a() { console.log('a1'); b(function(b2) { requestAnimationFrame(function() { con

承诺对于在异步调用的末尾附加某些内容非常有用,而异步调用不需要知道它的跟随者。可以在更一般或更复杂的情况下使用承诺,在异步调用中间需要回调,如果可以,那么它能提供相同级别的分离吗?

例如,假设您有两个函数a和b,其中执行顺序从a转移到b,再返回两次

使用回调:

function a() {
    console.log('a1');
    b(function(b2) { 
        requestAnimationFrame(function() { 
            console.log('a2'); 
            b2(); 
        }); 
    });
}

function b(a2) {
    console.log('b1');
    a2(function() { 
        requestAnimationFrame(function() { 
            console.log('b2'); 
        }); 
    });
}

我知道这两个函数可能会被分成两部分,并用承诺串在一起,但这可能会丢失在a1/b1中设置的计算范围变量的重要开销,或者导致冗余代码,或者更难遵循执行顺序


问题是,是否可以使用承诺重写此代码,使b()不需要了解a(),但在b()执行之前,调用方有机会插入一些内容?

在回答之前,我稍微修改了您的示例以突出执行流:

function a() {
    console.log('a1');
    var c = function(b2) { 
        var d = function() { 
            console.log('a2'); 
            b2(); 
        });
        requestAnimationFrame(d);
    }
    b(c);
}

function b(a2) {
    console.log('b1');
    var e = function() { 
        var f = function() { 
            console.log('b2'); 
        });
        requestAnimationFrame(f);
    }
    a2(e);
}
这是相同的代码,除了我将四个匿名函数命名为
c
d
e
f
,因为我想引用它们

执行顺序如下:

  • 调用一个
  • a
    同步调用
    b
  • b
    同步调用
    c
  • c
    调用
    requestAnimationFrame
    (异步API)
  • requestAnimationFrame
    返回
  • c
    返回
  • b
    返回并执行完毕
  • a
    返回并执行完毕
  • 稍后,当动画帧可用时,会发生以下情况:

  • 调用
    d
  • d
    同步调用
    e
  • e
    调用
    requestAnimationFrame
  • 等等
  • 当然,
    e
    可以访问
    b
    中定义的变量,由于JavaScript闭包,这些变量比原始
    b
    的执行时间长

    同样,在JavaScript中,我们将
    a
    视为一个异步操作,在
    a
    启动的所有操作完成后结束,但其中只有两个异步步骤(4和11),因此这两个步骤将受益于承诺

    现在谈谈你的问题:在b不知道a的情况下,承诺能提供相同的结果和分离吗?是的,但这与您的示例相比并不公平,因为它会在不需要同步调用时将您的同步调用放大为异步调用:

    function a() {
        console.log('a1');
        var b2;
        b().then(function(result) {
            b2 = result;
            return new Promise(function(r) { requestAnimationFrame(r); })
        }).then(function() { 
            console.log('a2'); 
            return b2();
        }).catch(function(e) {
            console.log(e.message); // error-handling
        });
    }
    
    function b() {
        console.log('b1');
        return Promise.resolve(function() {
            return new Promise(function(r) {
                requestAnimationFrame(r);
            }).then(function() { 
                console.log('b2'); 
            });
        });
    }
    
    这里的诀窍是b返回a中稍后要执行的步骤,这是一种可以重复的模式


    这是一个好的模式吗?如果不了解更多关于原始用例的信息,很难说。对于异步调用,我还没有遇到一个不能从承诺中获益的调用,但对于同步调用,我不会,因为它会改变执行顺序(除非可能只有一个异常值,其余的都是异步的,这有助于可读性)。

    抱歉,但我不了解您试图添加到
    b()中的内容
    在不了解
    a()
    的情况下提供更大的灵活性。您能否举例说明如何在没有承诺的情况下手动添加灵活性?这可以通过使用回调或承诺来实现。如果您希望
    a
    b
    彼此解耦,那么请使用流程管理器来协调整个流程。这可以通过使用“蹦床”来实现,蹦床或多或少是以不同的形式做出的承诺-函数(无论来自何处)充当“继续”action.@plalx如果我们有一些函数可以做的事情,让我们在操作完成之前,但在它们执行时执行操作,那就好了。如果只有函数可以将控制权让给其他函数就好了。哦,等等-我们有这个,它叫做生成器:D这里甚至是一个例子:只有在这里它才意识到函数——它实现了承诺的功能。协同程序只在每个阶段使用一个钩子就可以干净地完成它。我建议你将不同的步骤分解成它们自己的函数。如果存在大量共享和临时数据,则创建一个对象,将数据放入对象中,并使每个步骤成为对象上的方法,以便所有步骤都可以轻松访问所有数据。然后,您可以简单地使用普通函数调用或承诺(如果步骤是异步的)对步骤进行排序。您可以根据需要使方法尽可能小或细粒度,以允许您根据需要交织其他内容。这使得每一步的代码独立于其他操作,然后你可以分别排序。你的分析很好!你在回答我的问题,在承诺中回报承诺是使用承诺的解决方案。如果没有开场白,也不参考评论中的建议,你的答案会更好。开场白是画了一条不存在的硬线——我所说的回访a的意思是访问a的范围,这一点你很清楚。提到线程是不合适的。a&b不会在第8步“永远”结束,因为它们的作用域仍然存在。我不介意这个意见,但是评论中的建议以重复我在问题中所说的话作为结束,并说我不是在问这个问题。我使a()和b()的序言同步只是为了使代码尽可能短,而不是为了将我的问题的范围和程度限制得如此严格。我的错不是你的错。我将requestAnimationFrame放入其中以强制进行一些异步。如果同步部分a()和b()的前导也是异步的,那么答案会怎样?排除回调的可能性,仅使用承诺是否合理?如果链条变长了怎么办?谢谢你帮我看到不存在强硬路线。我已经根据你的反馈编辑了我的答案。我仍然可以公开同步步骤,因为这与我认为的答案有关,但我对其他方式持开放态度。模式