Javascript 如何在动态承诺列表中等待最后一个承诺?

Javascript 如何在动态承诺列表中等待最后一个承诺?,javascript,angularjs,promise,angular-promise,cancellation,Javascript,Angularjs,Promise,Angular Promise,Cancellation,我有一个函数F,它启动一个异步进程X。该函数返回一个在X结束时解析的承诺(我通过X返回的承诺来学习) 当X的(w.l.o.g.)第一个实例X1正在运行时,可能会有更多对F的调用。每个调用都会产生一个新的X实例,例如X2、X3等等 现在,困难在于:当创建X2时,根据X1的状态,X1应该结束或中止。X2应仅在X1不再处于活动状态时开始工作在任何情况下,只有在X2结束时,或者在X2运行时再次调用F时,才应解析从之前所有调用F返回的未解析承诺。或者,如果X的任何后续实例结束,则应解析该承诺。 到目前为止

我有一个函数F,它启动一个异步进程X。该函数返回一个在X结束时解析的承诺(我通过X返回的承诺来学习)

当X的(w.l.o.g.)第一个实例X1正在运行时,可能会有更多对F的调用。每个调用都会产生一个新的X实例,例如X2、X3等等

现在,困难在于:当创建X2时,根据X1的状态,X1应该结束或中止。X2应仅在X1不再处于活动状态时开始工作在任何情况下,只有在X2结束时,或者在X2运行时再次调用F时,才应解析从之前所有调用F返回的未解析承诺。或者,如果X的任何后续实例结束,则应解析该承诺。

到目前为止,对F的第一个调用调用
$q.defer()
来创建一个deferred,它的承诺由对F的所有调用返回,直到最后一个X结束。(然后,应该解析延迟的函数,并将包含它的字段重置为null,等待对F的下一个调用集群。)

现在,我的问题是等待X的所有实例完成。我知道我可以使用<代码> $q.ALL < /代码>,如果我事先有完整的X实例列表,但是我必须考虑稍后调用F,这不是一个解决方案。理想情况下,我应该
,然后
-将某个东西链接到X返回的承诺,以解决延迟问题,并在将该函数链接到稍后的X实例时立即“取消锁定”

我想是这样的:

var currentDeferred = null;

function F() {
    if (!currentDeferred) {
        currentDeferred = $q.defer();
    }

    // if previous X then "unchain" its promise handler
    X().then(function () {
        var curDef = currentDeferred;
        currentDeferred = null;
        curDef.resolve();
    });

    return currentDeferred.promise;
}
然而,如果这是正确的解决方案,我不知道如何执行“不受约束”

我该怎么做?我是否错过了承诺的一些常见模式,甚至是内置功能,或者我完全走错了方向?


添加一点上下文:调用F以加载数据(异步)并更新一些可视化输出。F返回一个承诺,该承诺只应在视觉输出再次更新到稳定状态(即不再有待定更新)时才予以解决

调用F来加载数据(异步)并更新一些可视化输出。F返回一个承诺,该承诺只应在视觉输出再次更新到稳定状态(即不再有待定更新)时才予以解决

由于
F
的所有调用者都将收到他们需要使用的承诺,但您只想在所有堆叠的调用完成后更新UI,最简单的事情是让每个承诺解析(或拒绝),并使用一个值告诉调用者,如果有另一个“获取更多数据”调用挂起,则不要更新UI;这样,只有承诺最后解析的调用方才会更新UI。您可以通过跟踪未完成的呼叫来做到这一点:

let accumulator = [];
let outstanding = 0;
function F(val) {
  ++outstanding;
  return getData(val)
    .then(data => {
      accumulator.push(data);
      return --outstanding == 0 ? accumulator.slice() : null;
    })
    .catch(error => {
      --outstanding;
      throw error;
    });
}
//假数据加载
函数getData(val){
返回新承诺(解决=>{
setTimeout(解析,Math.random()*500,“数据代表”+val);
});
}
设累加器=[];
设未完成=0;
函数F(val){
++杰出的;
返回getData(val)
。然后(数据=>{
累加器。推送(数据);
return--suspended==0?acculator.slice():null;
})
.catch(错误=>{
--杰出的;
投掷误差;
});
}
//下面是我们测试调用的解析和拒绝处理程序
常数解析=数据=>{
log(“链完成:”,数据?(“更新:”+data.join(“,”):“不更新”);
};
const rejected=error=>{//This从未被调用,我们不拒绝
控制台错误(error);
};
//一个电话:
F(“a”)。然后(解决)。捕获(拒绝);
设置超时(()=>{
//一个后续呼叫
控制台日志(“---”);
F(“b1”)。然后(解决)。捕获(拒绝);
F(“b2”)。然后(已解决)。捕获(已拒绝);
}, 600);
设置超时(()=>{
//随后的两个电话
控制台日志(“---”);
F(“c1”)。然后(解决)。捕获(拒绝);
F(“c2”)。然后(解决)。捕获(拒绝);
F(“c3”)。然后(解决)。捕获(拒绝);
}, 1200);
。作为控制台包装器{
最大高度:100%!重要;

}
@T.J.Crowder:我刚刚添加了一个示例,说明我是如何想象这一切的。@T.niese:我刚刚添加了一个示例。我认为,从代码示例中可以明显看出,将新承诺链接到最后一个承诺并不容易,因为一旦前一个承诺运行,我对
currentderferred
的引用就消失了,并且
currentderferred
已经解决(即使如果X的另一个实例即将出现,也不应该如此)。“我可能应该
,然后
-将某些内容链接到承诺,然后“取消锁定”该功能”-是的,这是理想情况下的工作方式,但不幸的是,本机承诺不支持取消作为“取消链接”“回电。不过,您可以尝试支持此用法,这正是您想要的。如果我理解正确,您有一个“中国版旋转器”问题的变体。在这个变体中,(1)板块一旦开始,可能不会复活,但可以添加新的板块以保持活动,(2)当所有板块都掉落时,活动结束。@Roamer-1888:感谢您指出这一点。我添加了“w.l.o.g.”以减少读者的困惑。这看起来很有希望(没有双关语)。事实上,我认为只有在“未完成”为0时,我才能解决延迟问题,而不是向F的早期调用方发回任何信号,同时使用此技术跟踪是否有任何未完成的F调用。@O.R.Mapper:很高兴这有帮助。我认为根本不解决延迟的问题将违反承诺的约定,即它将解决或拒绝。话虽如此,但我看不出这一点。不过,该规范有意将其最小化,重点放在可互操作的
然后
方法上。我还想知道内存的含义,尽管这可能是我的偏执(通过内存分析,你可以很容易地分辨出来)。哦,我将解析产生X1的F调用返回的承诺,但不在X1末尾的承诺处理程序中(但仅在e的承诺处理程序中)