<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angularjs@1" data-semver="1.5.0" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="MainCtrl">
<h3>Succeeded</h3>
<div ng-repeat="line in results">
<p ng-if="line.succeeded">{{line.msg}}</p>
</div>
<h3>Failed</h3>
<div ng-repeat="line in results">
<p ng-if="!line.succeeded">{{line.msg}}</p>
</div>
</body>
</html>
var app = angular.module('plunker', []);
app.config(function($provide) {
$provide.decorator("$q", function($delegate) {
//Helper method copied from q.js.
var isPromiseLike = function(obj) {
return obj && angular.isFunction(obj.then);
}
/*
* Adapted from the post and comments at http://www.codeducky.org/q-serial/
* @description Execute a collection of tasks serially. A task is a function that returns a promise
*
* @param {Array.<Function>|Object.<Function>} tasks An array or hash of tasks. A tasks is a function
* that returns a promise. You can also provide a collection of objects with a success tasks, failure task, and/or notify function
* @returns {Promise} Returns a single promise that will be resolved or rejected when the last task
* has been resolved or rejected.
*/
function serial(tasks) {
//Fake a “previous task” for our initial iteration
var prevPromise;
var error = new Error();
var results = [];
var lastTask = function() {
var endPromise = $delegate.defer();
endPromise.resolve(results);
return endPromise.promise;
}
tasks = tasks.concat(lastTask);
angular.forEach(tasks, function(task, key) {
var success = task.success || task;
var fail = task.fail;
var notify = task.notify;
var nextPromise;
//First task
if (!prevPromise) {
nextPromise = success();
if (!isPromiseLike(nextPromise)) {
error.message = "Task " + key + " did not return a promise.";
throw error;
}
} else {
//Wait until the previous promise has resolved or rejected to execute the next task
nextPromise = prevPromise.then(
/*success*/
function(result) {
results.push(result);
if (!success) return results;
var ret = success(result);
if (!isPromiseLike(ret)) {
error.message = "Task " + key + " did not return a promise.";
throw error;
}
return ret;
},
/*failure*/
function(reason) {
if (!fail) {
return $delegate.reject(reason);
}
var ret = fail(reason);
if (!isPromiseLike(ret)) {
error.message = "Fail for task " + key + " did not return a promise.";
throw error;
}
return ret;
},
notify);
}
prevPromise = nextPromise;
});
return prevPromise || $delegate.when();
}
$delegate.serial = serial;
return $delegate;
});
});
app.controller('MainCtrl', function($scope, $q) {
$scope.results = [];
var ctrl = this;
ctrl.intermediateResults = [];
function getMyPromise(i) {
return function() {
var response = {
succeeded: false,
msg: ''
};
var deferred = $q.defer();
/* The problem with $q.all() is that it runs things in parallel. We want serial */
// to simulate error, every 7th request fails
if (i % 7 === 0) {
response.succeeded = false;
response.msg = 'error ' + i;
deferred.resolve(response);
} else {
response.succeeded = true;
response.msg = 'hello ' + i;
deferred.resolve(response);
}
return deferred.promise;
}
}
var promises = [];
for (var i = 1; i <= 15; i++) {
console.log("added promise");
promises.push(getMyPromise(i));
}
$q.serial(promises).then(function(response) {
console.log("RESULTS:" + JSON.stringify(response));
$scope.results = response;
})
.catch(function(error) {
console.log("ERROR:" + error);
});
});
/* Styles go here */