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

que*_*zak 12 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?
Run Code Online (Sandbox Code Playgroud)

脚本继续解析promise3,即使最后all(...).catch()抛出!有人可以解释为什么吗?我可以做些什么来阻止其他承诺中的任何一个拒绝?

tri*_*cot 6

Promises/A+ 规范中不包括取消承诺。

但是,一些 Promise 库有这样的取消扩展。以蓝鸟为例:

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
    });
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdn.jsdelivr.net/bluebird/latest/bluebird.core.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

请注意,即使 promise3 被取消,它的setTimeout回调仍然会被调用。但它不会触发thencatch回调。就好像这个承诺永远不会达成……永远。

如果您还想停止触发计时器事件,则这与 Promise 无关,可以使用clearTimeout. BluebirdonCancel在 Promise 构造函数中公开了一个回调函数,当一个 Promise 被取消时它会调用这个函数。因此,您可以使用它来删除计时器事件:

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
    });
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdn.jsdelivr.net/bluebird/latest/bluebird.core.min.js"></script>
Run Code Online (Sandbox Code Playgroud)