Search code examples
javascriptpromiseecmascript-6es6-promise

Promise.all not returning


I have been trying to get Promise.all to work with no success with a list of promises, so instead tried it with just an array of one promise instead and I get the same issue:

  let tasks = [];
  tasks.push(function(resolve, reject){
    superagent
    .post(URL_ROOT + url_path)
    .send(data)
    .end(function(err, res){
          if(err)
            reject(err);

          assert.equal(res.status, status.UNAUTHORIZED); //401
          console.log('Promise completed successfully');
          resolve();
    });
  });


Promise.all([
  new Promise(tasks[0])
]).then( function(){
  console.log("Done");
  done();
})
.catch( function(reason){
  throw new Error(reason);
});

"Promise completed successfully" prints just fine but then it just hangs, and 'Done' never prints.

Any help would be much appreciated.


Solution

  • Look at the node handler:

    .end(function(err, res){
          if(err)
            reject(err);
    
          assert.equal(res.status, status.UNAUTHORIZED); //401
          console.log('Promise completed successfully');
          resolve();
    });
    

    This will call both reject and resolve if there was an error (and the assertation succeeds). I suspect this in your case, so the promise gets rejected (as reject is called before resolve), the code continues and prints Promise completed successfully.

    After that, the promise chain runs into the rejection handler:

    .catch( function(reason){
      throw new Error(reason);
    });
    

    But this code doesn't do anything, as throwing in a promise continuation will translate into a rejection in the resulting promise, which promise is forgotten about here.

    Try the following to validate my theory, and see if it logs:

    .catch( function(reason){
      console.log("Promise rejected");
      throw new Error(reason);
    });
    

    To fix this, all you have to do is to restructure your code a bit:

    .end(function(err, res){
      if(err) {
        reject(err);
      } else {
        resolve();
        assert.equal(res.status, status.UNAUTHORIZED); //401
        console.log('Promise completed successfully');
      }
    });
    

    So you have converted the asynchronous task to a proper promise (might have to handle .on("error", reason => reject(reason)) as well), and put an error handler in the catch clause.

    If you still want to pass the error to the global error handler, the best you can do is to do it in a setTimeout, so the promise callback can't catch and translate the error:

    .catch( function(reason) {
      setTimeout(() => {
        throw new Error(reason);
      }, 0);
    });