How to Chain and Share Prior Results With Promises

How to chain and share prior results with Promises

There are a few models for dependent promises and passing data from one to the next. Which one works best depends upon whether you only need the previous data in the next call or whether you need access to all prior data. Here are several models:

Feed Result of One to the Next

callhttp(url1, data1).then(function(result1) {
// result1 is available here
return callhttp(url2, data2);
}).then(function(result2) {
// only result2 is available here
return callhttp(url3, data3);
}).then(function(result3) {
// all three are done now, final result is in result3
});

Assign Intermediate Results to Higher Scope

var r1, r2, r3;
callhttp(url1, data1).then(function(result1) {
r1 = result1;
return callhttp(url2, data2);
}).then(function(result2) {
r2 = result2;
// can access r1 or r2
return callhttp(url3, data3);
}).then(function(result3) {
r3 = result3;
// can access r1 or r2 or r3
});

Accumulate Results in One Object

var results = {};
callhttp(url1, data1).then(function(result1) {
results.result1 = result1;
return callhttp(url2, data2);
}).then(function(result2) {
results.result2 = result2;
// can access results.result1 or results.result2
return callhttp(url3, data3);
}).then(function(result3) {
results.result3 = result3;
// can access results.result1 or results.result2 or results.result3
});

Nest, so all Previous Results Can Be Accessed

callhttp(url1, data1).then(function(result1) {
// result1 is available here
return callhttp(url2, data2).then(function(result2) {
// result1 and result2 available here
return callhttp(url3, data3).then(function(result3) {
// result1, result2 and result3 available here
});
});
})

Break the Chain into Independent Pieces, Collect Results

If some parts of the chain can proceed independently, rather than one after the other, then you can launch them separately and use Promise.all() to know when those multiple pieces are done and you then will have all the data from those independent pieces:

var p1 = callhttp(url1, data1);
var p2 = callhttp(url2, data2).then(function(result2) {
return someAsync(result2);
}).then(function(result2a) {
return someOtherAsync(result2a);
});
var p3 = callhttp(url3, data3).then(function(result3) {
return someAsync(result3);
});
Promise.all([p1, p2, p3]).then(function(results) {
// multiple results available in results array
// that can be processed further here with
// other promises
});

Sequence with await in ES7

Since the promise chain is just a mechanism for sequencing asynchronous operations, in ES7, you can also use await and then the intermediate results are all available in the same scope (perhaps simpler than the separate scopes of the chained .then() handlers):

async function someFunction(...) {

const r1 = await callhttp(url1, data1);

// can use r1 here to formulate second http call
const r2 = await callhttp(url2, data2);

// can use r1 and r2 here to formulate third http call
const r3 = await callhttp(url3, data3);

// do some computation that has access to r1, r2 and r3
return someResult;
}

someFunction(...).then(result => {
// process final result here
}).catch(err => {
// handle error here
});

How do I access previous promise results in a .then() chain?

ECMAScript Harmony

Of course, this problem was recognized by the language designers as well. They did a lot of work and the async functions proposal finally made it into

ECMAScript 8

You don't need a single then invocation or callback function anymore, as in an asynchronous function (that returns a promise when being called) you can simply wait for promises to resolve directly. It also features arbitrary control structures like conditions, loops and try-catch-clauses, but for the sake of convenience we don't need them here:

async function getExample() {
var resultA = await promiseA(…);
// some processing
var resultB = await promiseB(…);
// more processing
return // something using both resultA and resultB
}

ECMAScript 6

While we were waiting for ES8, we already did use a very similar kind of syntax. ES6 came with generator functions, which allow breaking the execution apart in pieces at arbitrarily placed yield keywords. Those slices can be run after each other, independently, even asynchronously - and that's just what we do when we want to wait for a promise resolution before running the next step.

There are dedicated libraries (like co or task.js), but also many promise libraries have helper functions (Q, Bluebird, when, …) that do this async step-by-step execution for you when you give them a generator function that yields promises.

var getExample = Promise.coroutine(function* () {
// ^^^^^^^^^^^^^^^^^ Bluebird syntax
var resultA = yield promiseA(…);
// some processing
var resultB = yield promiseB(…);
// more processing
return // something using both resultA and resultB
});

This did work in Node.js since version 4.0, also a few browsers (or their dev editions) did support generator syntax relatively early.

ECMAScript 5

However, if you want/need to be backward-compatible you cannot use those without a transpiler. Both generator functions and async functions are supported by the current tooling, see for example the documentation of Babel on generators and async functions.

And then, there are also many other compile-to-JS languages
that are dedicated to easing asynchronous programming. They usually use a syntax similar to await, (e.g. Iced CoffeeScript), but there are also others that feature a Haskell-like do-notation (e.g. LatteJs, monadic, PureScript or LispyScript).

Promise chaining: Use result from previous promise in next then callback

In my opinion, the zen of promises is all about figuring out they're really just asynchronous values. If you start using them as such these problems become simpler in many cases. It's not a silver bullet but it sure does help:

In ES5:

var student = Student.find();
var helpRequest = student.then(HelpRequest.findByStudent);
Promise.all([student, helpRequest]).then(function(results){
var student = results[0];
var helpRequest = results[1];
// access both here
});

In ES6, with all its features:

var student = Student.find();
var helpRequest = student.then(HelpRequest.findByStudent);
Promise.all([student, helpRequest]).then(([student, helpRequest]) => {
// access both here
});

In another richer promise library (bluebird):

var student = Student.find();
var helpRequest = student.then(HelpRequest.findByStudent);
Promise.join(student, helpRequest, function(student, helpRequest){
// access both here
});

NodeJS: Chain functions automatically in a promise?

Since you said it may be a variable length array and you show sequencing the requests, you can just loop through the array using async/await:

async function do_api_get_requests(API_IDS) {
for (let id of API_IDS) {
const data = await axios.get(`https://my.api.com/${id}`);
// do something with data here
}
return "success";
}

And, since you said the list of API ids would be variable, I made it a parameter that you can pass into the function.


If you wanted to run all the API requests in parallel (which might be OK for a small array, but might be trouble for a large array) and you don't need to run them in a specific order, you can do this:

function do_api_get_requests(API_IDS) {
return Promise.all(API_IDS.map(async (id) => {
const data = await axios.get(`https://my.api.com/${id}`);
// do something with data here for this request
})).then(() => {
// make resolved value be "success"
return "success";
});
}

Depending upon your circumstances, you could also use Promise.allSettled(). Since you don't show getting results back, it's not clear whether that would be useful or not.

Passing Variables Through a Promise Chain

You could try using Promise.all() which you can pass an array of promises and it provides an array of responses within the then() callback when all promises passed in have resolved. You can access those array values to pass into functionC:

Promise.all([functionA, functionB]).then(values => functionC(values[0], values[1]));

Might be a little cleaner (without nesting) as it doesn't look like the response from functionA needs to be passed into functionB.

Otherwise, nesting would look like:

return functionA().then(foo => {
return functionB().then(bar => {
return functionC(foo, bar);
});
});

Waiting for result in promise chain consisting of .fetch(), .all() and .()then

Ignoring the fact that you have a spelling issue with media_reponse, you should really call .json() on the response as soon as you have it. So do:

  // ....
.then((media_links) => {
return Promise.all(media_links.map((url) => fetch(url).then(response => response.json()));
})

This will produce a promise that resolves to all the data.

How to pass variables through promise chain

I'll explain why your code didn't work.

The scope of the 'let' variable remains between those brackets that you've put.
Thus, in the next .then() that message variable has lost its scope and you couldn't get it to work.

Read the docs here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

To solve your issue you can declare the variable outside the promise chain and write it like so:

let message; // Declare it outside so that the variable is in scope throughout the promise chain

return foo
.bar(baz)
.then((firstResult) => {
message = firstResult; // Set the value here
})
.then(() => foo
.bar(qux)
.then((secondResult) => {
message += secondResult;
console.log(message);
}));

Refer to the following for a working example: https://jsfiddle.net/sivcan/h3pguw4e/

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

Using array destructuring to name variables:

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

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

That alone is much clearer to follow, and I also removed the redundant Promise.resolve(), as non-promise values in the array passed to Promise.all() will automatically be wrapped in that already.

Using async / await:

Assuming your version of Node.js is >= 7.6 (or you're using a 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);
})
}

into this:

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);
}

Wait on all promises in a promise chain

When inside a .then() handler, you have to return a promise if you want it to be linked into the chain and have its value become the fulfilled value.

Though it's not entirely clear to me from your question what you're trying to achieve, it seems like maybe what you want to do is this:

function1ThatReturnsAPromise().then(function(array) {
console.log("First function finished");
return array;
}).then(function(value) {
return function2ThatReturnsAPromise(value);
}).then(function(finalValue) {
// you can process the finalValue here which will be the last
// value returned or the fulfilled value of the last promise returned
});

If you want to accumulate values from multiple promises, then there are several separate design patterns for that. Here are prior answers that show several different ways to do that.

Chaining Requests and Accumulating Promise Results

How do I access previous promise results in a .then() chain?



Related Topics



Leave a reply



Submit