There are many ways to make good control flows with ES2015 promises compared to alternatives like "callback hell" and similar. Lots of examples out there.
However, when the arguments for each step in the control flow depend on more than the return/resolved value of the previous step, it is harder to find a good syntax. Actual use cases are usually a bit more complex, so the simplified example syntaxes might not look that ugly, but it makes it easier to explain and discuss.
So, the question is how to make a simple (readable and easily maintainable) but flexible way of solving this kind of cases.
Example
Three operations in the control flow. Each being a function that returns a Promise
.
function firstOperation(arg1) {
return new Promise(...);
}
function secondOperation(firstResponse) {
return new Promise(...);
}
function thirdOperation(firstResponse, secondResponse) {
return new Promise(...);
}
Simpler control flows
If each step just depended on the previous one, it could look simething like this:
firstOperation('foo')
.then(res1 => secondOperation(res1))
.then(res2 => thirdOperation(res2));
Or even simpler:
firstOperation('foo')
.then(secondOperation)
.then(thirdOperation);
No problem there. But in this case, thirdOperation require arguments from both of the first two operations.
The future
In ES8, I guess this could look something like:
const res1 = await firstOperation('foo');
const res2 = await secondOperation(res1);
const res3 = await thirdOperation(res1, res2);
The present
I want to use the completed standards, so I hope to find the best possible solution for these kind of cases with ES2015 syntax or possibly a promise/generator library.
Possible (but not very simple/readable) ways to solve it:
firstOperation('foo')
.then(res1 => secondOperation(res1)
.then(res2 => thirdOperation(res1, res2))
);
Making it a kind of "promise hell" that works, but in more complex cases (more than one level) would become ugly and hard to read/maintain.
Another solution:
firstOperation('foo')
.then(res1 => secondOperation(res1)
.then(res2 => ({ res1, res2 })))
.then(({ res1, res2 }) => thirdOperation(res1, res2));
Not much prettier, the only benifit compared to the previous one, is that if there where more than three operations, they could all be called on the same level, instead of indenting one level further each time. And after each operation, the result is being merged with the other ones into a "context object" keeping all the results.
Suggestions?
So, until a standardized way comes along (ES8 probably), I guess using some kind of promise/generator library would be an acceptable solution. What I mostly hope, is that I won't need to touch the functions themselves. Like still letting them (firstOperation
, secondOperation
and thirdOperation
in the examples) get arguments the normal way, and return a promise that resolves with the value directly, instead of having to rewrite them to be able to fit different control flow use cases.
Suggestions for solving cases like this?