Returning objects created by chained javascript pr

2019-07-10 00:01发布

问题:

I am struggling to wrap my head around chaining together promises to achieve a desired result.

Brief background: I'm Using Ionic2 (based on Angular2) to create mobile app. Data persistence is based on SQLite. In order to re-build a complex object that contains nested arrays, I need to chain together a number of database calls.

buildObjectFromID(id) {

    return new Promise(function (resolve, reject) {
        let db = new DBHelper();

        try {

          // Get the event object from id
          db.getEventWithCMSID(id).then(event => {

            db.getBannerForOwner(event.cmsId).then(banner => {
              event.banner = banner;
            });
            db.getImagesForOwner(event.cmsId).then(images => {
              event.images = images;
            });

            db.getProfilePicturesForOwner(event.cmsId).then(profilepictures => {
              event.profilepicture = profilepictures;
            });

            db.getLogosForOwner(event.cmsId).then(logos => {
              event.logos = logos;
            });

            resolve(event);

          });
        }
        catch
          (err) {
          reject({err: err});
        }
      }
    );
  }

This method aims to fetch a main object from the database, and using it's ID, fetches and appends its related properties from additional tables. I wish to rebuild the object in it's entirety before passing the result back.

However, at the moment, the object is passed back and then over time the properties are added once each additional call is completed.

I would really appreciate if someone could inform me how I can chain these together, so that the controller calling 'buildObjectFromID' gets a complete object.

Many thanks.

回答1:

Two changes you can make:

  1. Remember that then returns a new promise. Since you already have a promise from db.getEventWithCMSID, you don't need to use new Promise at all, just use the one you get from calling then on that one. In general, before reaching for new Promise, consider whether you already have one the work with.

  2. To wait for all of your subordinate operations to complete, use Promise.all.

So:

buildObjectFromID(id) {
    let db = new DBHelper();

    return db.getEventWithCMSID(id).then(event => {
        return Promise.all([
            db.getBannerForOwner(event.cmsId).then(banner => {
              event.banner = banner;
            }),
            db.getImagesForOwner(event.cmsId).then(images => {
              event.images = images;
            }),
            db.getProfilePicturesForOwner(event.cmsId).then(profilepictures => {
              event.profilepicture = profilepictures;
            }),
            db.getLogosForOwner(event.cmsId).then(logos => {
              event.logos = logos;
            })
        ]).then(() => {
            return event;
        });
    });
}

Live Example on Babel's REPL (for brevity I left out two of the subordinate calls, just include banner and images)

That also has the advantage of propagating failures, which your original code didn't do (consider what happens if getBannerForOwner fails, for instance).

Live Example on Babel's REPL demonstrating failure



回答2:

I would leverage the Promise.all method and chain different promises like this:

buildObjectFromID(id) {
  let db = new DBHelper();

  // Get the event object from id
  return db.getEventWithCMSID(id).then(event => {
    return Promise.all([
      event,
      db.getBannerForOwner(event.cmsId),
      db.getImagesForOwner(event.cmsId),
      db.getProfilePicturesForOwner(event.cmsId),
      db.getLogosForOwner(event.cmsId)
    ]);
  }).then(result => {
    let event = result[0];
    let banner = result[1];
    let images = result[2];
    let logos = result[3];

    event.banner = banner;
    event.images = images;
    event.profilepicture = profilepictures;
    event.logos = logos;
    return event;
  });
}