Closed r1b closed 8 years ago
Promises are already started values. In your case, both aChain, bChain and cChain area already executing.
Control flow can be accomplished in a variety of ways, namely an if/else
works :)
Is this a feature you would consider were someone were to make a pull request? We have a solution that we'd like to offer.
Feel free to offer the solution, no guarantees
Promise.prototype.branch = function(...fns) {
return this.all().spread((index, ...values)=>{
if(index===null ||
index === undefined ||
typeof fns[index] !== 'function') return Promise.reject(values);
return fns[index].apply(this, values);
})
}
new Promise.resolve().then(nothing=>{
return [2, 'somevalue', 'anothervalue']
})
.branch(a, b, c, d)
.done(value=>{
console.log(value);
})
function a(value) {
console.log('a', value);
return 0;
}
function b(value) {
console.log('b', value);
return 1;
}
function c(valuea, valueb) {
console.log('c', valuea, valueb);
return 2;
}
function d(value) {
console.log('d', value);
return 3;
}
It's used much like spread, in that you return an array from a previous link in the chain. The array is simple: [index, ...values]. The index corresponds to the branch, and the value is arbitrary.
This seems like it would be better handled with a combinator function branch
:
function branch(...fns) {
return function([index, ...values]) {
if (index === null ||
index === undefined ||
typeof fns[index] !== 'function') return Promise.reject(values);
return fns[index].apply(this, values)
}
}
import {branch} from 'promise-combinators'
new Promise.resolve().then(nothing=>{
return [2, 'somevalue', 'anothervalue']
})
.then(branch(a, b, c, d))
The benefit is that it works with any promise library
edit: Tried to fix errors.
I might be misunderstanding your solution, when I run it caseId is undefined.
I would prefer branch being used in place of then, but I can see how your solution can work, and has syntactic sugar.
@Celadora Sorry, I forgot to separate the case id from the rest of the arguments.
Should be fixed now, using better variable names and rest arguments.
Hey @benjamingr @spion @r1b @Celadora I'd like to suggest an alternative pattern:
.thenIf()
(and related idea: .tapIf()
)
// Either use like so:
const checkEmail = email => Promise.resolve(email)
.thenIf(e => e.length > 5)
// Or like so:
const checkEmail = email => Promise.resolve(email)
.then(thenIf(e => e.length > 5))
function thenIf(cond, ifTrue = (x) => x, ifFalse = (x) => null) {
return value => Promise
.resolve(cond(value))
.then(ans => ans ? ifTrue(value) : ifFalse(value))
})
}
Promise.prototype.thenIf = thenIf;
Here's how this could be helpful refactoring the .props()
docs example into something like:
function directorySizeInfo(dir) {
const counts = {dirs: 0, files: 0, totalSize: 0, min: {size: Infinity}, max: {size: -Infinity}};
const checkMin = s => counts.min = s.size > 0 && s.size < counts.min.size ? s : counts.min;
const checkMax = s => counts.max = s.size > 0 && s.size > counts.max.size ? s : counts.max;
const totalInc = s => counts.totalSize += s.size;
return {
files: getStats(dir),
counts: {dirs: counts.dirs, files: counts.files},
smallest: counts.min,
largest: counts.max,
totalSize: counts.totalSize,
}
function getStats(dir) {
return fs.readdirAsync(dir)
.map(fileName => {
return Promise
.resolve(path.join(dir, fileName))
.then(fs.statAsync)
.tap(s => [totalInc, checkMax, checkMin].map(fn => fn(s)))
.thenIf(stat => stat.isDirectory(),
stat => {
counts.dirs++;
return getStats(stat.filePath);
}, stat => {
counts.files++;
return stat;
})
}).then(_.flatten);
}
}
Frequently I find myself wanting to add some control flow to my promise chains, e.g:
Is this something you could see in bluebird?
Best, Rob