0

I am trying to understand promises, it appears as if all promises I have looked at in examples follow a waterfall pattern, where the results of the previous function are passed to the .then function, and the results of that function are passed to the .then function again and again.

What if I need to do certain things in a certain order, but the next function doesn't count on the output of the previous promise?

const obj = {
    key1: value1,
    key2: value2
};

const files = ['file1', 'file2', 'file3'];

Promise.all(files.map(makeBackups))
.then(
  // use obj to create new strings to be inserted into orig files after they were backed up - Could be done at same time orig files are being backed up
)
.then(
  // write to files now that they have a backup and content to be written
)
.catch(
  // something happened when trying to write a file.
)

What am I missing?

shaun
  • 1,223
  • 1
  • 19
  • 44
  • It's not clear what your question is. If you want to sequence things in a particular order, then you sequence them whether the next one depends upon the results of the previous one or not. Your example shows 3 `makeBackups()` operations running at the same time. `files.map(makeBackups)` starts them all so they are all going at the same time. Then, `Promise.all()` tracks them all to tell you when they are all done. These are not sequenced in any specific order as they all run at the same time and can finish in any order. There are multiple design patterns for sequencing with promises. – jfriend00 Feb 06 '18 at 23:20
  • If you want to create the new strings immediately and not after all the backups, just don't put that code in the `then` callback?! – Bergi Feb 07 '18 at 00:03
  • use async/await will be better – yue you Feb 07 '18 at 00:31

1 Answers1

0

You understand it well. There is unfortunatelly no silver bullet, the easiest way to have some order, but do not rely on "what previous promise returned" is saving it in variable that has bigger scope (basically performing as global variable from point of view of promise chain)

function doIt() {
    let someVariable;
    Promise.all(files.map(makeBackups))
    .then((val) => (someVariable = val)) // saving something to val
    .then(
      // do something unrelated to the input, but in this order
    )
    .then(() => {
        if (someVariable > 5){ } // using the variable when you need it
    }
    .catch(
      // something happened when trying to write a file.
    )
}

If you can use newer version of Node.js, there is async await that makes your life a little easier

async function doIt() {
    const someVariable = await Promise.all(files.map(makeBackups))
    await somePromise// do something unrelated to the input, but in this order
    if (someVariable > 5){ } // using the variable when you need it
}

try-catch can be used over the whole doIt - it has one advantage, it catches both - the rejected promises (if you await them) and also bugs outside a promise (i.e. mistyping someVariable and throwing standard error)

libik
  • 22,239
  • 9
  • 44
  • 87
  • The global variable approach is horrible and there are [many much better solutions](https://stackoverflow.com/q/28250680/1048572) (`async`/`await` being one of them). Please remove the first part of your answer. – Bergi Feb 07 '18 at 00:05
  • @Bergi - can you explain why is it "horrible"? Do you realize, it has same scope as when using async await? – libik Feb 07 '18 at 07:37
  • Sorry, instead of using clear and simple solution (function-scope variable), now you have to manage to have .bind almost everywhere and also you have to make sure, that you are using bluebird promises and not native one. It does not help in any way (if you do not call ```this.something = value``` it fails in same manner as ```something = value```). Seems like very bad solution to me – libik Feb 07 '18 at 07:47
  • I was not recommending `bind`? In fact the answer tries to explain that Bluebird's `bind` does have the exact same problems, only changing variable scope for an object. The better approaches are in the other answers. – Bergi Feb 07 '18 at 07:49
  • @Bergi - lets see, yours solution - IMO typically overengineered, pass-through - thats one of the worst thing you can do, break the chain - good one, but not usable well in all situations – libik Feb 07 '18 at 07:52