简体   繁体   中英

Js : Chaining promises chains in a clear and readable way

Hello there overflowers,

I'm currently working on a project of mine, using a node server with a mongo data store. I'm currently writing some functions to populate the database for me.
Examples of objects in the database are Users,Classes,School Years,Students ratings, etc.
All the functions to create such objects are implemented like this :

    function popUser(users){
        chain = [];
        users.forEach((v,i,a)=>{
            let p = new Promise(function(res,rej){
                let newU = new User(v);
                newU.save(()=>{
                    err && rej(err);
                    res();
                });
            });
            chain.push(p);
        });
        return chain
    }

In the remainder of my population module i invoke such functions depending on my needs. The order in which the above functions are called is important, thus I don't want fully parallel execution of constructors.

With Promises I could do something like this :

popUser(users).then(popClasses).then(...). ... .catch((err)=>{})

With Promises Chains I know that I can do the following

Promise.all(usersChain).then(()=>{
    //make a new chain for Classes
    Promise.all(classesChain).then (()=>{},(err)=>{})
},(err)=>{})

I think we can agree that it becomes quite hard to read and understand, thus the question :

Is there a way of achieving the same results with a different, more readable syntax?

Edit : to be clearer, usersChain and cleassesChains are arrays of Promises to create and insert some object (or objects) into the database. I can not make a single chain because some objects might have to be inserted after some other objects have already been inserted.

Edit : Wait, can I just call

Promsie.all(populateCountersPromise).then(promise1).then(promise2).catch((err)=>{})

There are different parts in the code that won't work, or should not be written that way.

If you use shortcuts like this:

err && rej(err);
res();

You should know what they mean, because this is equal to:

if( err ) { 
  rej(err);
}
res();

So if an error occurs both rej and res are called.

From your popUser you return an array of Promises, so popUser(users).then(popClasses) would fail, because you cannot call .then on an array.

The first thing you should do, is to clean up your popUser function:

function popUser(users) {

  let promises = users.map((v, i, a) => {
    return new Promise(function(res, rej) {
      let newU = new User(v);
      newU.save(err => {
        if (err) {
          rej(err)
        } else {
          res()
        }
      });
    });
  });

  return Promies.all(promises)
}

Use .map instead a forEach with a push , because it makes it clear right from the beginning what you do. And use Promies.all to return a Promise from your function that waits for all users to be saved, if you do the same for popClasses you can write it like:

popUser(users)
   .then(popClasses)
   .then(...)
   .catch((err)=>{})

If you really want to write it like in your last code snippet, then change it to this:

Promise.all(usersChain)
  .then(() => Promise.all(classesChain))
  .then(() => {})
  .catch(err => {})

Many APIs now adays support both the classical callbacks and Promises, so you could improve your popUser further:

function popUser(users) {
  let promises = users.map(v => new User(v).save())
  return Promies.all(promises)
}

Mongoose callbacks are legacy API. Mongoose supports promises for a long time and doesn't need to be promisified.

For concurrent promises, forEach can be replaced with map , this is exact use case for the latter:

function popUser(users){
  return Promise.all(
    users.map(userData => new User(userData).save())
  );
}

async..await can be used in the rest of the cases where promises should be sequential:

try {
  await popUser(users);
  await popClasses();
  ...
} catch (err) {
  ...
}

Why not return Promise.all(chain) in function popUser .

Returning a Promise is better to match semantic of popUser which is a function of doing something asynchronously.Returning array of promise is confusing.

Then you can use popUsers.then(popClasses).then(...). ... .catch((err)=>{}) popUsers.then(popClasses).then(...). ... .catch((err)=>{}) to queue up promises

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM