我有一个函数F,它启动一个异步进程X.该函数返回一个在X结束时解析的promise(我通过X返回的promise来学习).
当(w.l.o.g.)X,X1的第一个实例正在运行时,可能会有更多对F的调用.每个调用都会产生一个新的X实例,例如: X2,X3等.
现在,困难在于:当创建X2时,根据X1的状态,X1应该结束或中止.只有X1不再处于活动状态时,X2才会开始工作.在任何情况下,从X的所有先前调用返回的未解析的promise应该只在X2结束时解决,或者,如果在X2运行时再次调用F,则X的任何后续实例都应该解析.
到目前为止,对F的第一次调用调用$q.defer()来创建一个延迟,其承诺由对F的所有调用返回,直到最后一个X结束. (然后,应该解析延迟,并且保存它的字段应该重置为null,等待下一个对F的调用集群.)
现在,我的问题是等到X的所有实例都完成了.我知道如果事先有完整的X实例列表,我可以使用$q.all,但是由于我必须考虑以后调用F,所以这不是解决方案.理想情况下,我应该把一些东西链接到X返回的promise以解决延迟,并且只要我将它链接到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 is called to load data (asynchronously) and updating some visual output. F returns a promise that should only be resolved once the visual output is updated to a stable state again (i.e. with no more updates pending).
由于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;
});
}
// Fake data load
function getData(val) {
return new Promise(resolve => {
setTimeout(resolve, Math.random() * 500, "data for " + val);
});
}
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;
});
}
// Resolution and rejection handlers for our test calls below
const resolved = data => {
console.log("chain done:", data ? ("update: " + data.join(", ")) : "don't update");
};
const rejected = error => { // This never gets called, we don't reject
console.error(error);
};
// A single call:
F("a").then(resolved).catch(rejected);
setTimeout(() => {
// One subsequent call
console.log("----");
F("b1").then(resolved).catch(rejected);
F("b2").then(resolved).catch(rejected);
}, 600);
setTimeout(() => {
// Two subsequent calls
console.log("----");
F("c1").then(resolved).catch(rejected);
F("c2").then(resolved).catch(rejected);
F("c3").then(resolved).catch(rejected);
}, 1200);
.as-console-wrapper {
max-height: 100% !important;
}
(这是本地承诺;根据需要调整$q.)
对我来说,“不更新”与“失败”不同,所以我使用了一个标志值(null)而不是拒绝来发信号.但是当然,您也可以使用带有标志值的拒绝,这取决于您. (这样可以将条件逻辑[这是一个真正的错误还是只是一个“不更新”?]放在你的catch处理程序而不是你的那个[这是真正的数据吗?] ……嗯,我现在可能会想到它.但这是微不足道的改变.)
显然,上面的累加器只是您真实数据结构的原始占位符(并且它不会尝试按照请求的顺序保持接收的数据).
我有承诺解决上面的数据副本(accumulator.slice()),但在您的情况下可能没有必要.