2

I have a situation where I think the only choice for me is to nest some Promises within each other. I have a Promise that needs to be performed and a method that does something until that Promise is complete. Something like this:

let promise = new Promise((resolve, reject) => {

  // Do some stuff

});

doSomethingUntilPromiseisDone(promise);

However, within my Promise, I need to execute another method that returns another Promise:

let promise = new Promise((resolve, reject) => {

  fetchValue(url)
    .then((value) => {

      // Do something here

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

});

doSomethingUntilPromiseisDone(promise);

But now, in the fetchValue method's then statement, I have another method I need to execute that, guess what, returns another Promise:

let promise = new Promise((resolve, reject) => {

  fetchValue(url)
    .then((value) => {

      saveToCache(value)
        .then((success) => {

          console.log('success!!');
          resolve('success');

        });

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

});

doSomethingUntilPromiseisDone(promise);

So in the end, I have a Promise, within a Promise, within a Promise. Is there someway I can structure this better so that it is more straightforward? It seems like nesting them within each other is counter to Promise's intended chaining approach.

Jake Wilson
  • 88,616
  • 93
  • 252
  • 370

3 Answers3

5

Use .then()

let doStuff = (resolve, reject) => {/* resolve() or reject() */};
let promise = new Promise(doStuff);
doSomethingUntilPromiseisDone(
  promise 
  .then(value => fetchValue(url))
  .then(value => value.blob())
  .then(saveToCache)
)
.then(success => console.log("success!!"))
.catch(err => console.error(err))
guest271314
  • 1
  • 15
  • 104
  • 177
  • If you are already using the arrow syntax, why are you still using `return`...? – Derek 朕會功夫 Oct 25 '16 at 05:59
  • @Derek朕會功夫 Good point. Not sure if `doSomethingUntilPromiseisDone` is necessary? – guest271314 Oct 25 '16 at 05:59
  • 1
    I just added spaces before `doSomethingUntilPromiseisDone` since the formatting was wrong. I don't know what that is actually. – Derek 朕會功夫 Oct 25 '16 at 06:00
  • 1
    If we are code-golfing, `catch` could just be `.catch(console.error)` and the first `then` would be just `.then(fetchValue)` – thefourtheye Oct 25 '16 at 06:06
  • 1
    Also, I have a feeling that this might not work for OP. – thefourtheye Oct 25 '16 at 06:10
  • `doSomethingUntilPromiseisDone` is necessary. Also in your answer, would `doSomethingUntilPromiseisDone` stop as soon as `promise` is done or would it continue until all the `then`'s are done as well? I need `doSomethingUntilPromiseisDone` to do its thing until the `saveToCache` is complete. – Jake Wilson Oct 25 '16 at 14:24
  • I have confirmed that this does not work. The problem with this approach is that `doSomethingUntilPromiseisDone` needs to wait until the entire Promise chain is complete. Not just the initial `promise` as shown in your example. – Jake Wilson Oct 25 '16 at 14:57
  • @JakeWilson Can you include the function body of `doSomethingUntilPromiseisDone` at Question? – guest271314 Oct 25 '16 at 15:48
  • It's the setStatusBarMessage method for the VSCode extension API. I have on idea what the inside of the method looks like. (https://code.visualstudio.com/Docs/extensionAPI/vscode-api#_window) – Jake Wilson Oct 25 '16 at 17:45
  • _"I have on idea what the inside of the method looks like."_ What does the function return? _"I have confirmed that this does not work."_ What is issue with pattern at Answer? Can you describe "does not work"? – guest271314 Oct 25 '16 at 18:09
1

you can use generator to flatten your nested promises (Bluebird.couroutine or Generators)

//Bluebird.couroutine
const generator = Promise.coroutine(function*() {
  try {
     const value = yield fetchValue(url);
     const success = yield saveToCache(value);
     console.log('success:', success);
  } catch(e) {
     console.error(err);
  }    
}));

generator();
Dmitriy
  • 340
  • 3
  • 9
0

Each function will call the next one with the result of the method before.

var promises = [1,2,3].map((guid)=>{
    return (param)=> {
      console.log("param", param);
      var id = guid;
      return new Promise(resolve => {
        // resolve in a random amount of time
        setTimeout(function () {
          resolve(id);
        }, (Math.random() * 1.5 | 0) * 1000);
      });
    }
}).reduce(function (acc, curr, index) {
  return acc.then(function (res) {
    return curr(res[index-1]).then(function (result) {
      console.log("result", result);
      res.push(result);
      return res;
    });
  });
}, Promise.resolve([]));
promises.then(console.log);
Cleriston
  • 750
  • 5
  • 11