0

Here I have a chain of promises that works fine. All the *.destroy's are promises that return promises:

function callDBDestroy() {
   var db;

   DB_Categories.destroy().then(function () {
      return DB_Equipment.destroy();
   }).catch(function (err) {
      showMsg("Error in callDBDestroy: " + err);
   }).then(function () {
      return DB_Certificates.destroy();
   }).catch(function (err) {
      showMsg("Error in callDBDestroy: " + err);
   }).then(function () {
      return DB_Locations.destroy();
   }).catch(function (err) {
      showMsg("Error in callDBDestroy: " + err);
   });
}

But I want to add an if statement into each one to check to see if the PouchDB database exists (which it doesn't if the DB_* is null).

If it exists, I want to destroy it then return (and these all return promises).

If it doesn't exist, I want to return an anonymous promise which returns nothing as none of the promises have any data I am concerned with.

In the example, I added in some sample code to do the if statement and I was wondering what I would put in the null instance that would pass a promise (resolve) value.

function callDBDestroy() {
   var db;


   DB_Categories.destroy().then(function () {
      if( DB_Equipment != null) {
          return DB_Equipment.destroy();
      }
      else {
          Anonymous empty promise - something like:

          new Promise().resolve();

      }
   }).then(function () {
      return DB_Certificates.destroy();
   }).then(function () {
      return DB_Locations.destroy();
   }).catch(function (err) {
      showMsg("Error in callDBDestroy: " + err);
   });
}

Thanks,

Tom

tshad
  • 335
  • 2
  • 4
  • 18
  • Assuming you would not want to continue through the rest of the chained promises if your DB is null, no? – Rob M. Mar 20 '17 at 22:48
  • Also, you can just call `Promise.resolve()`, you don't need the `new` stuff – Rob M. Mar 20 '17 at 22:49
  • Do you mean "*methods* that return promises"? – Bergi Mar 20 '17 at 22:55
  • do they have to be run in series? – Thomas Mar 20 '17 at 23:09
  • Since it looks like you are new here, if either of the answers you have below answered your question, then you can indicate that to the community by clicking the green checkmark next to the best answer. This will also earn you some reputation points here on stack overflow for following the proper procedure. – jfriend00 Mar 24 '17 at 04:07
  • In this case, they are in a series. The first set of code has a catch after each .destroy. I wanted check each DB variable to see if null. If not null, then I can delete the database. If not, I wanted to return a resolve, but this part isn't a promise so I thought I needed to create a promise anonymously to resolve it. – tshad Mar 28 '17 at 22:20

5 Answers5

1

It looks like you are just wondering how to manually resolve/reject a Promise. If that is the case you can just call Promise.resolve(optionalValue) or Promise.reject(optionalValue) if you want to go to the catch handler:

function callDBDestroy() {
   var db;

   DB_Categories.destroy()
   .then(function () {
      if( DB_Equipment != null) {
          return DB_Equipment.destroy();
      } else {
          return Promise.resolve();
      }
   }).then(function () {
      return DB_Certificates.destroy();
   }).then(function () {
      return DB_Locations.destroy();
   }).catch(function (err) {
      showMsg("Error in callDBDestroy: " + err);
   });
}
Rob M.
  • 35,491
  • 6
  • 51
  • 50
  • 1
    There is no point in `return Promise.resolve()` from a `then` as `Promise.resolve` is called automatically on the return value. One would just `return;`. – Benjamin Gruenbaum Mar 20 '17 at 22:54
  • @BenjaminGruenbaum Thanks for the note, agreed. I was trying to explain to OP how to manually resolve/reject Promises as it seemed like a point of confusion. – Rob M. Mar 20 '17 at 22:58
  • It was. So the a resolve is called event if I do "return;" or "return 'something'". – tshad Mar 28 '17 at 22:22
0

You could wrap it:

function withDb(db, handler) {
    return function onFulfilled(value) {
       if(db === null) throw new Error("DB Null");
       return handler(value);
    });
}

Which would let you do:

function callDBDestroy() {
   var db;
   var w = withDb(db); // or whatever instance

   DB_Categories.destroy().then(w(function () {
       // do stuff

    }))); // .then(w( to chain calls here.
    ...
}
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
0

I want to return an anonymous promise which returns nothing as none of the promises have any data I am concerned with. Something like:

new Promise().resolve();

You are looking for Promise.resolve(undefined). Though you can omit the undefined, that's implicit.

….then(function () {
    if (DB_Equipment != null) {
        return DB_Equipment.destroy();
    } else {
        return Promise.resolve(undefined);
    }
}).…

And you don't even have to return a promise from a then callback, simply returning undefined (or not returning) will have the same effect.

….then(function () {
    if (DB_Equipment != null) {
        return DB_Equipment.destroy();
    }
}).…

In your case, I'd recommend a wrapper function:

function destroyDatabase(db, name = "db") {
    if (db != null)
        return db.destroy().catch(err => {
            showMsg(`Error in destroying ${name}: ${err}`);
        });
    else
        return Promise.resolve();
}
function callDBDestroy() {
    return destroyDatabase(DB_Categories, "categories")
    .then(() => destroyDatabase(DB_Certificates, "certificates"))
    .then(() => destroyDatabase(DB_Locations, "locations"))
}
// or even in parallel:
function callDBDestroy() {
    return Promise.all([
        destroyDatabase(DB_Categories, "categories"),
        destroyDatabase(DB_Certificates, "certificates"),
        destroyDatabase(DB_Locations, "locations")
    ]);
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I had thought you had to have a return or the promises cause issues (side effects). I had found in some cases where I was having an issue with the code, adding a return solved it. – tshad Mar 28 '17 at 22:31
  • Yes, *if* you are doing something that uses promises, you (pretty much) always need to `return` them. However, when there is no database and you are not doing anything, then you don't need to return any results either. – Bergi Mar 28 '17 at 22:34
0

How about using an Array, since you do the very same task, and only the DB changes:

//serial
function callDBDestroy() {
    var databases = [
        DB_Categories,
        DB_Equipment,
        DB_Certificates,
        DB_Locations
    ];

    function errorMessage(err){ showMsg("Error in callDBDestroy: " + err) };

    databases.reduce(
        (prev, db) => db == null? 
            prev: 
            prev.then(() => db.destroy().catch(errorMessage)), 
        Promise.resolve()
    )
}

//parallel
function callDBDestroy() {
    var databases = [
        DB_Categories,
        DB_Equipment,
        DB_Certificates,
        DB_Locations
    ];

    function errorMessage(err){ showMsg("Error in callDBDestroy: " + err) };

    databases.forEach( db => db && db.destroy().catch(errorMessage) );
}

I've added a serial and a paralell version.

Thomas
  • 11,958
  • 1
  • 14
  • 23
0

It seems that you can DRY this out and replace a lot of redundant code by using an array of databases and then just loop through the array:

function callDbDestroy();
    var dbs = [DB_Categories, DB_Equipment, DB_Certificates, DB_Locations];

    // chain all the destroys together
    return dbs.reduce((p, db) => {
        return p.then(() => {
            if (db) {
                return db.destroy().catch(err => {
                    showMsg("Error in callDBDestroy: " + err);
                });
            }
        });

    }, Promise.resolve());
}

You do not have to return a promise from a .then() handler. If you just have no return value, then it's just like doing return undefined which just means that no value will be passed to the next .then() handler, but the promise chain will continue just fine. Conceptually, it works the same as return Promise.resolve(), but there's no need to make an extra promise there.

Since you aren't passing a value from one .then() to the next in the chain, you have nothing to pass there so you can just not return anything if there's no db value to call destroy on.

FYI, using .reduce() to loop through an array is with the return p.then(...) structure is a common design pattern for sequencing async operations on an array.

FYI, using the Bluebird promise library (which has some useful helpers), this could be done like this:

function callDbDestroy();
    var dbs = [DB_Categories, DB_Equipment, DB_Certificates, DB_Locations];

    return Promise.mapSeries(dbs, db => {
        if (db) {
            return db.destroy().catch(err => {
                showMsg("Error in callDBDestroy: " + err);
            });
        }
    });
}

For more info on why the Bluebird (or other promise libraries) are still useful even with ES6, see Are there still reasons to use promise libraries like Q or BlueBird now that we have ES6 promises?


Since it appears that these databases might all be independent, I'm wondering why you are forcing them to be executed in sequence. If they don't have to be forced into sequence, then you could do this:

function callDbDestroy();
    var dbs = [DB_Categories, DB_Equipment, DB_Certificates, DB_Locations];

    return Promise.all(dbs.map(db => {
        if (db) {
            return db.destroy().catch(err => {
                showMsg("Error in callDBDestroy: " + err);
            });
        }
    }));
}

Since this runs the operations in parallel, it has the opportunity for faster end-to-end execution time vs. strict serialization.

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I actually had thought of using the Promise.all, but wasn't sure how to set it up. I am not sure what map does. – tshad Mar 28 '17 at 22:27
  • @tshad - `.map()` is an array method. You can read about it [here on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). It takes an array as input and produces a new array - in this case I'm producing an array of promises which are then passed to `Promise.all()`. – jfriend00 Mar 29 '17 at 00:28