如何等待动态承诺列表中的最后一个承诺?
How to wait for the last promise in a dynamic list of promises?
我有一个启动异步进程 X 的函数 F。函数 returns 一个在 X 结束时解决的承诺(我通过 X 返回的承诺学习)。
虽然 (w.l.o.g.) X 的第一个实例 X1 是 运行,但可能会有更多对 F 的调用。每个调用都会产生一个新的 X 实例,例如X2、X3 等等。
现在,困难在于: 当创建 X2 时,根据 X1 的状态,X1 应该结束或中止。只有当 X1 不再处于活动状态时,X2 才应该开始工作。 在任何情况下,所有 之前对 F 的调用返回的未解决的承诺应该在 X2 结束后 仅 解决,以及- 或者,X 的任何后续实例,如果在 X2 为 运行.
时再次调用 F
到目前为止,对 F 的第一次调用调用 $q.defer()
来创建一个 deferred,其 promise 由所有对 F 的调用返回,直到最后一个 X 结束。 (然后,deferred 应该被 resolved 并且保存它的字段应该被重置为 null,等待下一个集群调用 F。)
现在,我的问题是等到 X 的所有实例都完成。我知道如果我事先有 X 实例的完整列表,我可以使用 $q.all
,但由于我必须考虑以后对 F 的调用,这不是这里的解决方案。理想情况下,我可能应该 then
将某些东西链接到 X 返回的承诺以解决延迟,并且 "unchain" 一旦我将它链接到 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;
}
但是,我不知道如何执行 "unchaining",如果这是正确的解决方案。
我该怎么做?我是否遗漏了 promise 的某些常见模式甚至内置功能,或者我完全走错了路?
添加一点上下文:调用 F 来加载数据(异步)并更新一些视觉输出。 F returns 只有在视觉输出再次更新到稳定状态(即没有更多更新挂起)后才应解决的承诺。
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,最简单的事情是让每个承诺如果有另一个 "get more data" 呼叫挂起,则用一个值告诉调用者不要更新 UI 来解析(或拒绝);这样,只有 promise 最后解决的调用者才会更新 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
进行调整。)
对我来说,"don't update" 不同于 "failed,",所以我使用标志值 (null
) 而不是拒绝来表示它。但当然,您也可以使用带有标志值的拒绝,这取决于您。 (这将有利于将条件逻辑 [“这是一个真正的错误还是只是一个 "don't update"?] 在你的 catch
处理程序而不是你的 then
[这是真实的数据还是不?]...嗯,我现在想起来可能会走另一条路。但那是微不足道的改变。)
显然,上面的 accumulator
只是您真实数据结构的粗略占位符(它不会尝试按照请求的顺序保存接收到的数据)。
我正在通过 copy 上述数据 (accumulator.slice()
) 解决承诺,但在您的情况下可能没有必要。
我有一个启动异步进程 X 的函数 F。函数 returns 一个在 X 结束时解决的承诺(我通过 X 返回的承诺学习)。
虽然 (w.l.o.g.) X 的第一个实例 X1 是 运行,但可能会有更多对 F 的调用。每个调用都会产生一个新的 X 实例,例如X2、X3 等等。
现在,困难在于: 当创建 X2 时,根据 X1 的状态,X1 应该结束或中止。只有当 X1 不再处于活动状态时,X2 才应该开始工作。 在任何情况下,所有 之前对 F 的调用返回的未解决的承诺应该在 X2 结束后 仅 解决,以及- 或者,X 的任何后续实例,如果在 X2 为 运行.
时再次调用 F到目前为止,对 F 的第一次调用调用 $q.defer()
来创建一个 deferred,其 promise 由所有对 F 的调用返回,直到最后一个 X 结束。 (然后,deferred 应该被 resolved 并且保存它的字段应该被重置为 null,等待下一个集群调用 F。)
现在,我的问题是等到 X 的所有实例都完成。我知道如果我事先有 X 实例的完整列表,我可以使用 $q.all
,但由于我必须考虑以后对 F 的调用,这不是这里的解决方案。理想情况下,我可能应该 then
将某些东西链接到 X 返回的承诺以解决延迟,并且 "unchain" 一旦我将它链接到 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;
}
但是,我不知道如何执行 "unchaining",如果这是正确的解决方案。
我该怎么做?我是否遗漏了 promise 的某些常见模式甚至内置功能,或者我完全走错了路?
添加一点上下文:调用 F 来加载数据(异步)并更新一些视觉输出。 F returns 只有在视觉输出再次更新到稳定状态(即没有更多更新挂起)后才应解决的承诺。
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,最简单的事情是让每个承诺如果有另一个 "get more data" 呼叫挂起,则用一个值告诉调用者不要更新 UI 来解析(或拒绝);这样,只有 promise 最后解决的调用者才会更新 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
进行调整。)
对我来说,"don't update" 不同于 "failed,",所以我使用标志值 (null
) 而不是拒绝来表示它。但当然,您也可以使用带有标志值的拒绝,这取决于您。 (这将有利于将条件逻辑 [“这是一个真正的错误还是只是一个 "don't update"?] 在你的 catch
处理程序而不是你的 then
[这是真实的数据还是不?]...嗯,我现在想起来可能会走另一条路。但那是微不足道的改变。)
显然,上面的 accumulator
只是您真实数据结构的粗略占位符(它不会尝试按照请求的顺序保存接收到的数据)。
我正在通过 copy 上述数据 (accumulator.slice()
) 解决承诺,但在您的情况下可能没有必要。