1

Promise.all returns a single Promise that fulfills when all of the promises passed as an iterable have been fulfilled. Having this in mind, how is it possible to check if only one of the promises is fulfilled? I have a piece of code shown below:

Promise.all([getPic('blah.jpg'), getPic('blah_two.jpg')]).then(
                function(result) {
                    $('#div').hide();
                }
            ).catch(function(result) {
                $('#div').show();
            })

Here, I want to wait for any of the images, that is blah.jpg and blah_two.jpg, to be loaded. If any of them is loaded then $('#div').hide();. However, my code checks for both of the images to be loaded, which is not what I actually have in mind. Could you please help me with the problem, and hide the $('#div') the moment any of the images is loaded.

There is one Promise.any which I think would do what I have in mind; however, as Mozilla says "is experimental and not fully supported by all browsers."

By the way, the following is my getPic function:

function getPic(src, div) {
        return new Promise(function(resolve, reject) {
            $(div).attr('src', src).on("error", function(e) {
                reject( "Cannot access " + src );
            })
                .on("load", function(e) {
                    resolve();
                });
        })
    }
H. M..
  • 568
  • 6
  • 15
  • `Promise.race()` (fulfill or reject) or `Promise.any()` (fulfill only) – Andreas May 04 '20 at 13:32
  • @Andreas, [Promise.any()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any) is not supported yet on all browsers and is still experimental, would be good to have fully supported though. – ROOT May 04 '20 at 13:40
  • Dear @palaѕн. This raceToSuccess function is what I have in mind. Yet, I cannot implement it on my code. May you please help me. – H. M.. May 04 '20 at 14:23

2 Answers2

1

If you support modern browsers, then you can use the Promise.race() method, returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.

Promise.race([getPic('blah.jpg'), getPic('blah_two.jpg')])
  .then(function(result) {
    $('#div').hide();
  }).catch(function(result) {
    $('#div').show();
  })

Here is the Browser compatibility chart. It's not supported in IE only.


To look for fulfilled promise only you use this oneSuccess() helper function like:

function oneSuccess(promises){
  return Promise.all(promises.map(p => {
    // If a request fails, count that as a resolution so it will keep
    // waiting for other possible successes. If a request succeeds,
    // treat it as a rejection so Promise.all immediately bails out.
    return p.then(
      val => Promise.reject(val),
      err => Promise.resolve(err)
    );
  })).then(
    // If '.all' resolved, we've just got an array of errors.
    errors => Promise.reject(errors),
    // If '.all' rejected, we've got the result we wanted.
    val => Promise.resolve(val)
  );
}

and then pass the getPic array of images like:

oneSuccess([getPic('blah.jpg'), getPic('blah_two.jpg')])
  .then(function(result) {
    $('#div').hide();
  }).catch(function(result) {
    $('#div').show();
  })

Demo:

function oneSuccess(promises) {
  return Promise.all(promises.map(p => {
    return p.then(
      val => Promise.reject(val),
      err => Promise.resolve(err)
    );
  })).then(
    errors => Promise.reject(errors),
    val => Promise.resolve(val)
  );
}

// Case 1: First resolved promise
oneSuccess([
  Promise.resolve(1), Promise.reject(2), Promise.resolve(3)
]).then(result => console.log('First Success: ', result))

// Case 2: No resolved promise
oneSuccess([
  Promise.reject(1), Promise.reject(2), Promise.reject(3)
]).catch(error => console.log('All failed: ', error))
palaѕн
  • 72,112
  • 17
  • 116
  • 136
  • @palaSH. Promise.race returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects. This last part of Promise.race is not what I want. I want one of the promises to be fulfilled for sure. – H. M.. May 04 '20 at 13:43
  • 1
    I have updated the code on how you could implement the function. Hope it helps! – palaѕн May 04 '20 at 14:28
  • Dear @palaѕн. I shall appreciate for the time you spent to help me. You aced it. Thanks a bunch. But there still remains a question. When both of the images are available, the images are loaded immediately, and when just one is available it takes a second to show the div. Can you figure out what the problem is. If it is unavoidable, I will go with it as it is now. Let me thank you one more time. :x – H. M.. May 04 '20 at 14:58
  • I am sorry, but I could not figure out this specific issue here. – palaѕн May 04 '20 at 15:22
0

you can use Promise.race(), here is an example from the documentation:

var p1 = new Promise(function(resolve, reject) { 
    setTimeout(() => resolve('one'), 500); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(() => resolve('two'), 100); 
});

Promise.race([p1, p2])
.then(function(value) {
  console.log(value); // "two"
  // Both fulfill, but p2 is faster
});
ROOT
  • 11,363
  • 5
  • 30
  • 45
  • 1
    _"I want one of the promises two be fulfilled definitely."_ - That's not guaranteed with `Promise.race()` – Andreas May 04 '20 at 13:44
  • About Promise.race, I agree with @Andreas. – H. M.. May 04 '20 at 13:54
  • 1
    @H.M.., then if you want to any then you can use [promise-any-polyfill](https://github.com/T99/promise-any-polyfill#documentation), might be useful to you. – ROOT May 04 '20 at 14:02