Michael Fulton Michael Fulton - 5 months ago 11
Node.js Question

How can I manage Node.js async variable scope?

I have the following code for populating my test database. The goal is to save the parent document after each of the child documents to the parent can have a reference to them.

function saveRecipe(ingredients, directions, recipe, done) {
var ingredientSaveTasks = createSaveTasks(ingredients)
var directionSaveTasks = createSaveTasks(directions)

async.parallel([
(callback) => { async.series(ingredientSaveTasks, callback) },
(callback) => { async.series(directionSaveTasks, callback) }
], (err, results) => {
recipe.ingredients = results[0] // The returned ids for each ingredient
recipe.directions = results[1] // The returned ids for each direction
recipe.save(done)
})
}

function createSaveTasks(objs) {
var saveTasks = []
for (var i = 0; i < objs.length; i++) {
var saveTask = function (callback) {
var obj = Object.assign({}, objs[i])
obj.save((err, result) => {
callback(err, result._id)
})
}
saveTasks.push(saveTask)
}
return saveTasks
}


I've tried a few variations on this and I think it has to do with variable scope. However, I thought by deep copying my obj with var obj = Object.assign({}, objs[i]) would save a "real" copy of the object for later use inside the async function.

Depending on which of the many way I've tried to make this work I end up with one of the following errors:


TypeError: obj.save is not a function

TypeError: Cannot read property 'save' of undefined


I've seen some talk about using .bind() to control variable scope but I'm not sure how to use it in this case.

Answer

Bind return a new function and you can specify the context assigned to the function (the value that you obtain when call "this" keyword inside the function) and/or the arguments passed to the function. Bind(thisArg, ...arguments). So in your case:

var saveTask = function(callback){this.save(...)}.bind(obj[i])

With this you specify the context, so in that function your object will be accesible as this. Some examples:

(function a(){console.log(this)}).bind({key : 'value'})();

var a = function(){console.log(this)}.bind({key : 'value'});
a();