Saving list of objects synchronously


#1

Is there a way to save a list of models synchronously, waiting for response from previous save before saving another one?


#2

You might want to schedule them using Ember.run, check out the following link:

http://emberjs.com/guides/understanding-ember/run-loop/#toc_an-example-of-the-internals


#3

DS.Model.save returns a promise, so wait until the promise resolves to save the next model:

model.save(attrs).then(function() {
    nextModel.save();
}, function() {
    // error handling
});

#4

What if I have an array of 10 objects that I want to save one after another?


#5

Code:

var arr = [item1,item2,item3];

function finishFunction() {
    console.log('done');
}

function errorFunction() {
    console.log('error');
}

function runNextOrFinish() {
    var next = arr.shift();
    if (next) {
        next.save().then(runNextOrFinish, errorFunction);
    } else {
        finishFunction();
    }
}

function main() {
    runNextOrFinish();
}

Run:

main();

I would think something like that should work


#6

Also:

function chainPromise(promise, obj) {
  if (!promise) {
    return obj.save();
  } else {
    return promise.then(function() {
      return obj.save();
    });    
  }
}

var arr = [inst1, inst2, inst3, inst4];

var thisItem, rootPromise = null;
for (var i=0, len=arr.length; i<len; i++) {
  rootPromise = chainPromise(rootPromise, arr[i]);
}

#7

That’s kind of an interesting question. I think others have shown good examples of using promises. I’d love to hear about your use case. When I read the title I thought you meant saving a list of objects “transactionally”, but your description is clear.


#8

Reduce would work well in this case

var chainedPromises = models.reduce(function(previous, model){
    return previous.then(model.save);
}, Ember.RSVP.resolve());

Note, I haven’t actually run this :smile:


#9

I think you would need to bind the model.save to ensure thisArg is correct. E.g.

var chainedPromises = models.reduce(function(previous, model){
    return previous.then(model.save.bind(model));
}, Ember.RSVP.resolve());

Other than that, Map/Reduce programming FTW.


#10

I didn’t know you could do that with bind, very useful.


#11

Yeap, that’s the native bind. FYI it doesn’t work on some old browsers (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Browser_compatibility). There are some bind polyfills floating around if you need.

Also another FYI for anyone using bind to wrap up a callback, it’s generally a good idea to use Ember.run.bind because it guarantees you’re inside of a runloop, however in this case it’s not necessary because promises are always resolved in a runloop.