Passing intermediate data between promises in JS without using Promise.all and Promise.resolve

I'm kind of a JS noob, but so far I really like ES6 / React / Immutable being able to do functional programming and FRP, and in particular the api promise. I especially like the chain template .then

, for example somePromiseGenerator().then(...).then(...).catch(...)

. This is ideal when the flow of asynchronous calls is perfectly linear. However, I often want to pass results from the top of this chain to the end.

The suboptimal template I used looks like this:

somePromiseGenrator()
    .then(r => {
        const inter = makeSomeIntermediateResults(r);
        const newPromise = makeNewPromise(r);

        return Promise.all([newPromise, Promise.resolve(inter)]);
    })
    .then(r => {
        handleR0andR1(r[0], r[1]);
    })

      

This happens, for example, when I receive data from ElasticSearch and want to supplement it with things that are in SQL or Neo4J, or call a secondary API.

Using Promise.all

and especially Promise.resolve

seems like a waste of effort. Is there a better way to make this work?

+3


source to share


3 answers


Using array destruction to denote variables:

somePromiseGenrator()
    .then(r => {
        const inter = makeSomeIntermediateResults(r);
        const newPromise = makeNewPromise(r);

        return Promise.all([newPromise, inter]);
    })
    .then([newPromiseResult, intermediateResult]) => {
        handleR0andR1(newPromiseResult, intermediateResult);
    })

      

Only this is much clearer and I also removed the redundant Promise.resolve()

, since the values ​​without promises in the passed array before Promise.all()

will be automatically wrapped in this already.

Using / : async

await



Assuming your version of Node.js >= 7.6

(or you are using transpiler / bundler / polyfill that supports ES2016 features), you can convert the following:

function promiseChain() {
    return somePromiseGenrator()
        .then(r => {
            const inter = makeSomeIntermediateResults(r);
            const newPromise = makeNewPromise(r);

            return Promise.all([newPromise, inter]);
        })
        .then([newPromiseResult, intermediateResult]) => {
            return handleR0andR1(newPromiseResult, intermediateResult);
        })
}

      

in it:

async function promiseChain() {
    const r = await somePromiseGenerator();
    // I'm assuming this isn't a promise
    // since it was initially wrapped in Promise.resolve()
    const inter = makeSomeIntermediateResults(r);
    const newPromiseResult = await makeNewPromise(r);

    return handleR0andR1(newPromiseResult, inter);
}

      

+4


source


I think you can do something like this if you don't want to use Promise.all

:

function pack(){
    var args = arguments;
    if((args.length - 1) % 2 != 0){
        throw "Invalid input";
    }
    var obj = args[0],
        promises = [];
    for(var i = 1; i < args.length; i+=2){
        let promise = args[i],
            name = args[i+1];
        promises.push(promise.then(val => {
            obj[name] = val;
        }));
    }
    return Promise.all(promises).then(() => obj);
}

      

Then you can use it like this:



Promise
.resolve({})
.then(obj => {
    return pack(obj, genVal("a"), "a", genVal("b"), "b");
})
.then(obj => {
    return pack(obj, genVal("c"), "c");
})
.then(console.log);

/* {
       partA: "some value from part A",
       partB: "some value from part B",
       partC: "some value from part C"
   } */

      

https://jsfiddle.net/DerekL/ssr23mjy/

0


source


You can define inter

as a variable that has an extended scope, eg. make it a parameter in an expression that is called directly:

(inter => somePromiseGenerator()
    .then(r => {
        inter = makeSomeIntermediateResults(r);
        return makeNewPromise(r);
    })
    .then(r => handleR0andR1(r, inter))
)();

      

See also the options suggested in this accepted answer , of which mine is option 2.

0


source







All Articles