1

I'm working on nodejs app currently, one part of which, tests some api calls and then returns as a promise, then perform another function.

So - I'm looping through an array of promises with the following two functions:

Over all function for all apis

function testAllApis(apiList, counter = 0){
  return new Promise(function(fulfill, reject){
    console.log(counter);
    if(counter == apiList.length){
      console.log('test1');
      fulfill();
      console.log('test2');
    }
    else {
      testSingleApi(apiList[counter]).then(() => {
        testAllApis(apiList, counter + 1);
      }).catch((e) => {
        console.log(e);
        reject(e);
        testAllApis(apiList, counter + 1);
      })
    }
  })
}

Function for each individual array

function testSingleApi(thisApi){
  return new Promise(function(fulfill, reject){
    var apiUrl = '/api/' + thisApi.substr(7).slice(0, -3) + '/testapi/';
    var options = {
      hostname: serverHost,
      port: serverPort,
      path: apiUrl,
      method: 'GET',
    };
    var req = http.request(options, (res) => {
      console.log(res.statusCode);
      fulfill(res.statusCode);
    });
    req.on('error', (e) => {
      reject(e.message);
    });
    req.end();
  });
}

When I call this in the terminal it functions as intended, and console logs the success codes (200) of the api calls I am making, but after the third one, when 'counter' is equal to the length of the array, it goes into the if condition in the testAllApis function, console logs 'test1', then 'test2' and doesn't fulfill() at all.

Does anyone have any insight into this? I am still quite new to promises and tried searching for a solution to this online but it was quite a specific question so thought to post here instead.

jock.perkins
  • 469
  • 6
  • 23
  • return testAllApis(apiList, counter + 1); – Jonas Wilms Jun 23 '17 at 10:43
  • Don't you mean `return testAllApis(apiList, counter + 1);`? Otherwise it won't wait till it's complete, would it? – laurent Jun 23 '17 at 10:43
  • Thank you guys. I tried this and sadly it still hasn't helped me. Is this best practise though? I will ensure to do this in future. – jock.perkins Jun 23 '17 at 11:35
  • Same as @this.lau_, I would write: ```return testSingleApi(apiList[counter]).then(() => { return testAllApis(apiList, counter + 1); })``` Notice the "2" ```return```s – Gnujeremie Jun 23 '17 at 13:35
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Jun 23 '17 at 13:49

2 Answers2

1

It is easier to use reduce to run promises sequentially:

var funcs = apiList.map((api) => testSingleApi(api));

var promiseSerial = (funcs) =>
  funcs.reduce((promise, func) =>
    promise.then(result = func().then(Array.prototype.concat.bind(result))),
    Promise.resolve([]));

promiseSerial(promises)
  .then(...)
  .catch(...);
Alberto Trindade Tavares
  • 10,056
  • 5
  • 38
  • 46
0

You can avoid recursion and get cleaner and more manageable code if you use async/await.

If your node supports async/await, you can refactor you logic like this:

async function testAllApis(apiList){
    for(var i=0; i<apiList.length; i++)
        console.log(await testSingleApi(apiList[i]));
}

testAllApis(apiList).then(function(){
    console.log("all done at this point")
});

If your node does not supports async/await, you can use nsynjs module and change your code like this:

nsynjs = require('nsynjs');
...

function testAllApis(apiList, testSingleApi){
    for(var i=0; i<apiList.length; i++)
        console.log(testSingleApi(apiList[i]).data);
}

nsynjs.run(testAllApis,{},apiList,testSingleApi,function(){
    console.log("all done at this point");
})
amaksr
  • 7,555
  • 2
  • 16
  • 17