Joss Joss - 6 months ago 8
Node.js Question

"Iterating" throw promises does not let to generate different ids

Reading some amazing tutorials about promises, I've discovered that, if I need to interate throw some promises, I can't use forEach or some other "traditional" iteration mechanisms, I have to use Q library for node, I've to "iterate" using

Q.all
.

I've written a simple example in Nodejs Express in order to be sure I've understood promises:

var form = [
{'name':'FORM_NAME_1.1',
'label2':'FORM_LABEL_1.2'
},
{'name':'FORM_NAME_2.1',
'label2':'FORM_LABEL_2.2'
}
];
var params = ['params1','params2'];
var meta = ['meta1','meta2'];

app.get('/', (req,res) => {
return Q.all([
form.map((currentValue,index,arr) => {
req.id = Math.random(); //Random ID to be used in the next promises
console.log(currentValue);
return Form.insert(currentValue,req);
}),
params.map((currentValue,index,arr) => {
console.log(req.id);
return Field.insert(currentValue,req.id);
}),
meta.map((currentValue,index,arr) => {
console.log(req.id);
return Meta.insert(currentValue,req.id);
})
])
.catch((err) => next(err))
.done(() => console.log('It\'s done'));
});


Form.insert
code simply is a promise with a
console.log
call, the same for
Field.insert
and
Meta.insert


var Form = {
insert: (param1,req) => {
var deferred = Q.defer();
console.log('Goes throw Form');
deferred.resolve();
return deferred.promise;
}
}


The problem is that seems to iterate right but the dynamicly generated
id
does not change
along the promises, this is the console output:

Listening at port 3000...
{ name: 'FORM_NAME_1.1', label2: 'FORM_LABEL_1.2' }
Goes throw Form
{ name: 'FORM_NAME_2.1', label2: 'FORM_LABEL_2.2' }
Goes throw Form
0.3757301066790548
Goes throw Field
0.3757301066790548
Goes throw Field
0.3757301066790548
Goes throw Meta
0.3757301066790548
Goes throw Meta
It's done


Any ideas about what is going wrong? Thanks!!

Answer

the reason it is not working is because in first for loop, the req.id is set multiple times before other promises are started and and all of them end up using the last randomly generated value, change your code to:

app.get('/', (req,res) => {

  let process = (currentValue,index,arr) => {
    let reqCopy = {id: Math.random()}
    for(let attr in req)   // copy all the request attributes
      if(attr && attr!='id')
        reqCopy[attr] = req[attr]
    return Q.all([
      Form.insert(form[index],reqCopy),
      Field.insert(params[index],reqCopy),
      Meta.insert(meta[index],reqCopy)
      ])
  }
  return Q.all(form.map(process))
    .catch(next)
    .done(() => console.log('It\'s done'));
})

you would notice that I am copying all the attributes of req to clone reqCopy for I am not sure what attributes of req are required by the subsequent methods, but at the same time, single req.id would not work thanks to the async nature of code

Comments