当Promise.all()拒绝时,停止其他承诺

时间:2018-02-13 08:50:42

标签: javascript promise es6-promise

虽然关于Promise.all的所有问题都集中在如何等待所有承诺,但我想采取另一种方式 - 当任何承诺失败时,停止其他承诺,或者甚至停止整个剧本。

这是一个简短的例子来说明:



const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'resolve1');
}).then(a => { console.log('then1'); return a; });

const promise2 = new Promise((resolve, reject) => {
  setTimeout(reject, 2000, 'reject2');
}).then(a => { console.log('then2'); return a; });

const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'resolve3');
}).then(a => { console.log('then3'); return a; });

Promise.all([promise1, promise2, promise3])
  .then(values => { console.log('then', values); })
  .catch(err => { console.log('catch', err); throw err; });

// results:
// > "then1"
// > "catch" "reject2"
// > "then3"    <------- WHY?
&#13;
&#13;
&#13;

脚本继续解析promise3,即使最终all(...).catch()抛出!有人可以解释原因吗?我能做些什么来阻止他们拒绝的其他承诺呢?

3 个答案:

答案 0 :(得分:2)

如评论中所述promises cannot be canceled.

您需要使用第三方承诺库或rxjs observables。

答案 1 :(得分:2)

取消承诺不包含在Promises/A+ specification

然而,一些Promise库有这样的取消扩展。以bluebird

为例

&#13;
&#13;
Promise.config({ cancellation: true }); // <-- enables this non-standard feature

const promise1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 'resolve1');
}).then(a => { console.log('then1'); return a; });

const promise2 = new Promise((resolve, reject) => {
    setTimeout(reject, 2000, 'reject2');
}).then(a => { console.log('then2'); return a; });

const promise3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 3000, 'resolve3');
}).then(a => { console.log('then3'); return a; });

const promises = [promise1, promise2, promise3];

Promise.all(promises)
    .then(values => { 
        console.log('then', values); 
    })
    .catch(err => { 
        console.log('catch', err); 
        promises.forEach(p => p.cancel()); // <--- Does not work with standard promises
    });
&#13;
<script src="https://cdn.jsdelivr.net/bluebird/latest/bluebird.core.min.js"></script>
&#13;
&#13;
&#13;

请注意,即使取消了promise3,仍会调用其setTimeout回调。但它不会触发thencatch回调。就好像这个承诺永远不会解决......永远。

如果您还希望停止触发计时器事件,则这与承诺无关,可以使用clearTimeout完成。 Bluebird在Promise构造函数中公开了一个onCancel回调函数,它将在取消promise时调用。因此,您可以使用它来删除计时器事件:

&#13;
&#13;
Promise.config({ cancellation: true }); // <-- enables this non-standard feature

const promise1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 'resolve1');
}).then(a => { console.log('then1'); return a; });

const promise2 = new Promise((resolve, reject) => {
    setTimeout(reject, 2000, 'reject2');
}).then(a => { console.log('then2'); return a; });

const promise3 = new Promise((resolve, reject, onCancel) => { // Third argument (non-standard)
    var timer = setTimeout(resolve, 3000, 'resolve3');
    onCancel(_ => {
        clearTimeout(timer);
        console.log('cancelled 3');
    });
}).then(a => { console.log('then3'); return a; });

const promises = [promise1, promise2, promise3];

Promise.all(promises)
    .then(values => { 
        console.log('then', values); 
    })
    .catch(err => { 
        console.log('catch', err); 
        promises.forEach(p => p.cancel()); // <--- Does not work with standard promises
    });
&#13;
<script src="https://cdn.jsdelivr.net/bluebird/latest/bluebird.core.min.js"></script>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

如果不再可以实现承诺,则该过程将退出,因此有可能创建一个实现此目标的小帮手


function timeoutWhen(promises, bail) {
  const pending = promises
    .map(promise => Promise.race([ bail, promise ]))
  return Promise.all(pending)
}


const never = new Promise(() => {})
const done = Promise.resolve()

const cancel = new Promise(ok => setTimeout(ok, 1000))

timeoutWhen([ never, done ], cancel)
  .then(() => {
    console.log('done')
  })

即使never承诺永远无法解决,日志也会完成,然后退出。