简体   繁体   中英

How do I flatten a nested promise dependency?

I'm using mondora/asteroid through a node app to use Meteor DDP via a promise pattern.

I have the following code I am rewriting from callback style, into a promise style, but am stuck on how to flatten it.

asteroid.call('Pony.search', { params })
  .then(res => {
    if (res.length === 1) {
      // something
      asteroid.call('Pony.finish', { params })
        // this part feels wrong
        .then(res => {
          // something else
        });
    } else {
      // nope
    }
  })
  .catch(err => {
    console.error(err);
  });

There is a second asteroid.call & then inside the first promise response which is a promise. This part feels wrong, like it should be flat and not nested but I'm not sure how to get there.

edit:

Ended up using something like this (still not decided on whether to have the first then check for if length === 1 and potentially immediately reject it. Anyone know what is best practice on that?

asteroid.call('Pony.search', { params })
  .then(res => res.length === 1 ? res : Promise.reject())
  .then(res => asteroid.call('Pony.finish', { res[0].something }))
  .then(res => {
    // do something
  })
  .catch(err => {
    // handle the no found
    console.error(err);
  });

Instead of nesting callbacks, chain promises together with .then()

A note:

I'm not sure what Promise library you are using, but the idea is to return a rejected promise from the first .then() if there is an error, otherwise you return a successful promise. The promise library will then handle the error handling for you, going to the catch block if there is a rejected promise.

asteroid.call('Pony.search', { params })
  .then(res => {
    res.length === 1 ? return asteroid.call('Pony.finish', { params }) : Promise.reject();
  })
  .then(res => {
    //do stuff here
  })
  .catch(err => {
    console.error(err);
  });

edit:

The only issue is when you need to access both of the return values from the promises at the same time. When you flatten out a promise chain you lose access to the results from the previous promises.

You have a few options:

  1. If you don't need the previous result then flatten out the promise chain like I did here
  2. If you do need the previous value

    2a. And you don't care about the ordering of execution then use Promise.all([promise1, promise2])

    2b. And you do care about the ordering of the execution then you must use nested promises like you originally did.

If promises are nested, there is no difference between promises and callback.

Try changing your code to use promise chain:

asteroid.call('Pony.search', { params })
  .then(res => {
    if (res.length === 1) {
      // something
      let p1 = asteroid.call('Pony.finish', { params });
      return p1;
    }
    else {
      // nope
    }
  })

  .then (function SomeThingElse(res2){
    // something else
    // res2 is return value from nested asteroid.call

  })

  .catch(err => {
    console.error(err);
  });

Since the first resolve handler returns a Promise p1 , the invocation of the next functions in the chain ( function SomeThingElse ) is deferred until p1 is resolved .

Now expanding this example:

asteroid.call('Pony.search', { params })
  .then(res => {
    if (res.length === 1) {
      // something
      let p1 = asteroid.call('Pony.finish', { params });
      return p1;
    }
    else {
      // nope
    }
  })

  .then (function SomeThingElse(res2){
    // something else
    // res2 is return value from nested asteroid.call

  })

  .then (function AnotherFunc(res3){

  })

  .catch(err => {
    console.error(err);
  });

If SomeThingElse returns a Promise , invocation of AnotherFunc is delayed until that promise is resolved.

If SomeThingElse does not return a Promise , AnotherFunc would be invoked immediately with the same parameters as 'SomeThingElse' received. In other words, both SomeThingElse and AnotherFunc are invoked when p1 is resolved.

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