0

I have a chain of promises, where certain things happen on resolve and different things happen on reject, however sometimes I want to skip all the following then statements.

My code looks like this

await req.reduce((promise, audit) => {
    let globalData;
  return promise.then(_ => this.add(audit)
      .then((data)=> {
                          globalData = data; console.log('1'); 
                          return dropbox_functions.createFolder(data.ui, data)
                     }, 
           (error)=> {
                          failed.push({audit: audit, error: 'There was an error adding this case to the database'}); 
                          console.log('1.1'); i = i + 1; socket.emit('bulkAddUpdate', i/arrLen); 
                          throw new Error('There was an error adding this case to the database');
                     })
       .then((data)=>{
                         console.log('2');
                         return dropbox_functions.checkScannerFolderExists(audit.scanner_ui)
                     },
            (error)=>{
                         console.log('2.1');issues.push({audit: globalData, error: 'There was an error creating the case folder in dropbox'}); 
                         i = i + 1;socket.emit('bulkAddUpdate', i/arrLen); 
                         throw new Error('There was an error creating the case folder in dropbox');
                     })
       .then((data)=>{
                         console.log('3');
                         return dropbox_functions.moveFolder(audit.scanner_ui, globalData.ui)},
            (error)=>{
                         console.log('3.1');issues.push({audit: globalData, error: 'No data folder was found so an empty one was created'}); 
                         return dropbox_functions.createDataFolder(globalData.ui)
                     })
       .then(()=>    {
                         console.log('4');
                         success.push({audit:globalData}); 
                         i = i + 1;socket.emit('bulkAddUpdate', i/arrLen);},
          (error)=> {
                         issues.push({audit: globalData, error: 'Scanner folder found but items not moved'});console.log('4.1');
                    })
      .catch(function(error){
              console.log(error)
            })
    );
  }, Promise.resolve()).catch(error => {console.log(error)});

In the first then if the code goes into the reject case, I want to skip all the remaining thens. However it doesn't and therefore all the subsequent then happen and all fail. The only reject that should move on is in the 3rd then when I do want to return a promise.

Arthur Le Calvez
  • 413
  • 2
  • 7
  • 17
  • 1
    `.catch` is just a glorified `.then`. It calls it internally. – Andrew Li Dec 17 '18 at 12:36
  • 3
    Don't mix raw promises with async/await unless needed. It just makes everything complicated. – Estus Flask Dec 17 '18 at 12:56
  • Could you elaborate on what you mean please estus. The reason I do what I do is because there is throttling on the dropbox api, so i need to do things one by one – Arthur Le Calvez Dec 17 '18 at 13:06
  • @ArthurLeCalvez You should just use a `for (const audit in req) { … await … }` loop. There's [rarely](https://stackoverflow.com/a/44664037/1048572) a need to use `then` when you can just use `await`. – Bergi Dec 17 '18 at 21:46
  • See also [How to properly break out of a promise chain?](https://stackoverflow.com/a/29500221/1048572), [how to break promise chain](https://stackoverflow.com/q/28803287/1048572) or [Proper way to skip a then function in Q Promises](https://stackoverflow.com/q/21576862/1048572) – Bergi Dec 17 '18 at 21:52

1 Answers1

0

In general, your problem is that some errors are fatal and some are not. You want fatal errors to fall all the way to the bottom, but non-fatal errors to be handled in sequence.

Keep all your "fatal" errors at top level, and nest the non-fatal ones inside the then block. I also suggest never using the "then(fn,fn)" format - always specify your catches explicitly.

Example:

return fnA().then(dataA => {
    return fnB(dataA).catch(error => {
        console.log("fnB got a non-fatal error, I'm fixing it.");
        return fnFixB();
    });
})
.then(dataB => fnC())
.then(dataC => {
    return fnD(dataC).catch(error => {
        console.log("fnD didn't work, I'm fixing it.");
        return fnFixD();
    });
}).catch(error => {
    console.log("Sorry: one of fnA, fnFixB, fnC, or fnFixD must have broken.");
});

In this example, if fnA or fnC reject, you're dropping to the bottom; but, fnB and fnD produce errors that you can potentially recover from (although if your fixes fnFixB and fnFixD fail, you'll drop to the bottom as well).

Also, I would suggest breaking your breaking your code into smaller methods for readability: check out the article Error handling in long Promise chains for a great little example very similar to yours.

Elliot Nelson
  • 11,371
  • 3
  • 30
  • 44