0

I'm trying to get my head around accessing previous results in promises as outlined here How do I access previous promise results in a .then() chain?

I've managed to get this working with 2 promises, but when I add a 3rd that makes a POST request to an API the promise result is my post request and not the response from that post request.

Basically the flow of my app is. I insert an item into a DB. Using that insertId I then insert multiple more items into a database that are 'children' of the first insertId. But I also need to then send these values to an API. This API will then return me another ID which I will ultimately associate with my own previous insertID. The code is roughly as follows (I've removed some other logic and error handling for the sake of brevity).

let name = req.body.name;
let value = req.body.values;
let obj = {name:name};
let entries = [];
let a = db.items.insertItem(name);

let b = a.then((data) => {
    let insertId = data.insertId;
    let promises = [];
    values.forEach((val) => {
           entries.push({value:val)})
           promises.push( db.values.insertValuesForItem(insertId,val));
    })
    obj.entries = entries; 
    return promises;

})
let c =  b.then((data) => {
     return request.post(constants.devUrl,
    {
        headers:{
        'Authorization': 'bearer ' + constants.developerToken,
        'content-type': 'application/json'
        },
        json:obj
    });
});

Promise.all([a,b,c]).then(([resa,resb,resc]) => {
   //resc here contains the post request info e.g the post data and headers
   // what I really want from resc is the response from my post request
    res.redirect(200,'/items/' +  resa.insertId);
})

As I mentioned previously, in resc I actually need the response from the API request, not details of the request itself. Any ideas how I achieve that?

TommyBs
  • 9,354
  • 4
  • 34
  • 65
  • Aren't you losing the `data` from the `b.then` callback, therefore losing the `promises` array you previously constructed? You don't use `data` anywhere, and it's lost afterwards. I don't understand what you want to achieve with `return promises` – Adelin Jan 26 '18 at 07:25
  • I'm not sure I follow. All my data is getting correctly inserted into the database and the api request is sending the correct data so I don't appear to be losing anything. – TommyBs Jan 26 '18 at 07:28
  • 1
    `let b = ...`. Here b is a promise that, when fulfilled, gives us the `promises` array. So in the following statement `b.then((data)...`, `data` is actually **that array of promises**. And since you're not doing anything with `data` inside the `b` success callback, I don't understand why you return in in the first place. – Adelin Jan 26 '18 at 07:31
  • I think my reasoning is that I need b to complete before I can call c as I need my `obj` to be constructed successfully with all the required info. But either way I'm not sure why that would be preventing me from access the response from c – TommyBs Jan 26 '18 at 07:33
  • 1
    I also wish Jeremy would have read the question where I state my reasoning for opening a new question especially as I link to the question that was marked – TommyBs Jan 26 '18 at 07:36
  • 1
    What lib are you using for `request`? Isn't `post` returning a promise? Perhaps you could chain a `.then` to your `post()` return value – Adelin Jan 26 '18 at 07:41
  • I'm using request https://github.com/request/request I thought about using a .then on the return value, but then doesn't that negate the point of promise.all as I end up nesting the promise again? In fact if I do resc.then() it throws an error of resc.then is not a function. The same if I do that in the assignment to c – TommyBs Jan 26 '18 at 07:43
  • You are definitely chaining incorrectly, but for what you need, to get the response, is this `request.post({url:'http://service.com/upload', form: {key:'value'}}, function(err,httpResponse,body){ /* ... */ })` Basically, a callback function to the `request` method will give you access to the `httpResponse` – Adelin Jan 26 '18 at 07:46
  • @Adelin I know I could use the callback, but request also supports promises, so shouldn't I be able to access this via a promise? – TommyBs Jan 26 '18 at 07:47
  • 1
    Not if you use it as-is. If you'd like it to return a promise, you need to use [the following libs](https://github.com/request/request#promises--asyncawait) – Adelin Jan 26 '18 at 07:49
  • @Adelin ah thanks, I guess that's where the confusion has come from. Shame this question now redirects off to the original as I guess it might have helped someone – TommyBs Jan 26 '18 at 07:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/163926/discussion-between-adelin-and-tommybs). – Adelin Jan 26 '18 at 07:53

1 Answers1

2

Now that the question was reopened, reiterating what I said in the comments:

You have two problems in your code:

  1. Incorrectly chaining the promises:

    let b = a.then((data) => {
        let insertId = data.insertId;
        let promises = [];
        values.forEach((val) => {
               entries.push({value:val)})
               promises.push( db.values.insertValuesForItem(insertId,val));
        })
        obj.entries = entries; 
        return promises; // this variable is accessed later as `data`, but
                         // never actually used => it's pointless
    
    })
    

    Since you return promises, you are in fact returning a Promise that, only when fulfilled, will return your array of promises. This means that:

    1. inside the b success callback, you are getting data (which is promises array) but not doing anything with it.
    2. inside Promise.all([a,b,c]).then(([resa,resb,resc]), resb is the array of promises, not the results of the db execution.

    I understand you're trying to synchronize the execution, but it's not the right approach.

  2. Incorrectly expecting request.post to return a promise:

    let c =  b.then((data) => {
         return request.post(constants.devUrl,
        {
            headers:{
            'Authorization': 'bearer ' + constants.developerToken,
            'content-type': 'application/json'
            },
            json:obj
        });
    });
    

    As you mentioned in the comments, you're using this requests lib. As such, c in this case, is indeed a Promise, but its return value is not httpResponse as you expect.

    • If you want to work with requests so that it returns promises, you need to use one of these variances of the requests lib, which are returning promises.

    • If, however, you don't want to change the lib, you can synchornize the execution by also passing a callback function for the post method. For example

      var gotResponseCallBack = function(err, httpResponse, body){
          return httpResponse;
      }
      request.post(constants.devUrl,
      {
          headers:{
          'Authorization': 'bearer ' + constants.developerToken,
          'content-type': 'application/json'
          },
          json:obj
      }, gotResponseCallBack); 
      
Adelin
  • 7,809
  • 5
  • 37
  • 65