I went with this code, which seems to work but just doesn't feel "right". Namely the way I detect whether or not the updates have finished to avoid calling context.savechanges() more than one. Please feel free to improve my answer!
function downloadTasksFromWeb(viewModel){
$org.context.UserSetting.first().then(function (userSetting) {
viewModel.set("currentUserSettings", userSetting);
backofficeUrl = viewModel.get("currentUserSettings.BackOfficeUrl") + "/api/tasks";
var operatorId = viewModel.get("currentUserSettings.OperatorId");
var rowsToProcess = 0, rowsProcessed = 0;
viewModel.messages.push("Connecting to server.");
showNotificationInfo("Connecting to server.");
jQuery.ajax({
type: "GET",
url: backofficeUrl,
dataType: 'json',
async: false,
username: "user",
password: "pw",
data: {"operatorId": operatorId},
success: function (data) {
viewModel.messages.push("Tasks received, saving to local device.");
showNotificationInfo("Tasks received, saving to local device.");
rowsToProcess = data.length;
$.each(data, function(idx, task) {
var existingTasks = $org.context.Task.filter("Id", "==", task.TaskId).toArray();
existingTasks.then(function(result) {
var taskToUpdate = $org.context.Task.attachOrGet({ Id:task.TaskId});
taskToUpdate.TaskType = task.TaskType;
taskToUpdate.StatusId = task.TaskStatusId;
taskToUpdate.TaskStatus = task.TaskStatus;
taskToUpdate.DateScheduled = task.Date;
taskToUpdate.TimeSlot = task.Time;
taskToUpdate.LastUpdated = new Date();
taskToUpdate.TaskName = "Job " + task.TaskId + " " + task.TaskType + " @" + task.AddressOfTask + ", " + task.PropertyPostCode;
taskToUpdate.SpecialInstructions = task.SpecialInstructions;
taskToUpdate.PropertyAddress = task.AddressOfTask;
taskToUpdate.PropertyPostCode = task.PropertyPostCode;
taskToUpdate.PropertyType = task.PropertyType;
taskToUpdate.NumberOfBedrooms = task.NumberOfBedrooms;
taskToUpdate.HasGarage = task.HasGarage;
taskToUpdate.HasOutHouse = task.HasOutHouse;
if (result.length == 0) {
$org.context.Task.add(taskToUpdate);
}
rowsProcessed++;
if (rowsProcessed == rowsToProcess) {
$org.context.saveChanges({
success: function(db) {
viewModel.messages.push("Tasks saved to local device.");
showNotificationInfo("Tasks saved to local device.");
}, error: function(err) {
console.log(err);
viewModel.messages.push("Errors saving tasks: " + err);
showNotificationError("Errors saving tasks: " + err);
}
});
}
});
});
}
}).fail(function(resultData) {
showNotificationError("There was an error communicating with the server. Please check your settings and try again.");
});
});
}