1

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.

6
  • 1
    What are ingredients and directions variables passed to createSaveTasks? Commented Jun 28, 2016 at 21:28
  • Arrays of Mongoose model objects, which contain a save function. If I put ingredients[0].save() as the first function of saveRecipe(), this ingredient is saved in the database. (just tried it to verify it works) Commented Jun 28, 2016 at 21:40
  • 1
    Ok, so what happen if you log objs[i] in the loop of createSaveTask function? Commented Jun 28, 2016 at 21:45
  • 1
    Well, so as you said bind is the solution, bind return a new function where 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. 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" Commented Jun 28, 2016 at 22:03
  • 1
    This worked perfectly! And now I understand what bind does, redefines the "this" keyword as you said. Thanks a lot, just copy/paste your answer and I'll accept. :-) Commented Jun 28, 2016 at 22:18

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.