Stop other promises when Promise.all() rejects

别来无恙 提交于 2020-01-02 06:49:26

问题


While all the questions about Promise.all focus on how to wait for all promises, I want to go the other way -- when any of the promises fails, stop the others, or even stop the whole script.

Here's a short example to illustrate:

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?

The script continues to resolve promise3, even though the final all(...).catch() throws! Can someone explain why? What can I do to stop the other promises at the point any of them rejects?


回答1:


Cancellation of promises is not included in the Promises/A+ specification.

However, some Promise libraries have such a cancellation extension. Take for example bluebird:

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

Note that even though promise3 is cancelled, its setTimeout callback will still be called. But it will not trigger the then or catch callbacks. It will be as if that promise never comes to a resolution ... ever.

If you want to also stop the timer event from triggering, then this is unrelated to promises, and can be done with clearTimeout. Bluebird exposes an onCancel callback function in the Promise constructor, which it will call when a promise is cancelled. So you can use that to remove the timer event:

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



回答2:


As stated in the comments promises cannot be canceled.

You would need to use a third party promise library or rxjs observables.



来源:https://stackoverflow.com/questions/48762751/stop-other-promises-when-promise-all-rejects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!