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

4.7k views Asked by At

I'm using straight ES6 Promises (with the es6-promise polyfill library) and I'm running into a problem with accessing results from previous promises in chained ones.

This problem is identical in the context of Angular/Q, but I'm dissatisfied with the answer and wanted to see if there's a better way:

How to access result from the previous promise in AngularJS promise chain?

Consider the code snippet below:

Student.find().then(function(student) {
        return HelpRequest.findByStudent(student);
    }, function(error) { //... }
).then(function(helpRequest) {
    // do things with helpRequest...
    // PROBLEM: I still want access to student. How can I get access to it?
});

In the chained promise, I want to use the student object that I got in the first promise. But as written, this can't access it. I have a couple apparent options:

  1. store the student in a variable in an outer scope (yuck)
  2. I actually don't know how this would work, but the solutions in the other question suggest I can call then on the result of HelpRequest.findByStudent() and Promise.resolve the combined result inside the Student.find().then call. The below implementation won't work I think, though.

    Student.find().then(function(student) {
            var data = {student: student};
            HelpRequest.findByStudent(student).then(function(helpRequest) {
                data.helpRequest = helpRequest;
            });
            // PROBLEM: if HelpRequest.findByStudent(student) is asynchronous, how 
            // does this get the data before returning?
            return data; 
        }, function(error) { //... }
    ).then(function(helpRequest) {
        // do things with helpRequest and student
    });
    

I absolutely don't want to do the processing of the helpRequest nested inside of the Student.find() method, as that defeats the purpose of chaining Promises; and even if the second example can be worked into a usable state, it still feels like a hack.

Is there a better way to do achieve this without having to introduce global state or nesting into my code? For instance, is there a way to call Promise.resolve() on multiple values, some of which may be promises and some of which are not?

I'm curious, hope I have alternatives/can understand how to make this work properly without introducing nesting or state!

1

There are 1 answers

3
Benjamin Gruenbaum On BEST ANSWER

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