-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathangular-async-hooks.js
executable file
·145 lines (112 loc) · 3.36 KB
/
angular-async-hooks.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
(function(window, angular, undefined) {
'use strict';
/**
* @ngdoc overview
* @name ngAsyncHooks
* @description
* # ngAsyncHooks
*
* Provides placement for async hooks to happen from
* external modules with a promise based system.
*/
angular.module('ngAsyncHooks', [])
.factory('asyncHooks', function($q, $timeout) {
var m = {},
tasks = [];
function AsyncHook(event, callback, parallel) {
parallel = typeof parallel === "boolean" ? parallel : true;
this.event = event;
this.callback = callback;
this.parallel = parallel;
}
/**
* Wrap callbacks to check they were created properly
* for better error reporting.
* @param {function} callback The callback to wrap
* @return {promise} Returns the callback which should be a promise
*/
function wrapCallback(callback) {
return function() {
var c = callback();
if (typeof c.then !== "function") {
throw new Error("callbacks must return a deferred.promise.");
}
return c;
}
}
function performSeries(series) {
var deferred = $q.defer(),
hookChain;
// let hookChain have a "then"
hookChain = deferred.promise;
// build then hook chain -- wrap each callback for additional error checking
series.forEach(function(hook) {
hookChain = hookChain.then(wrapCallback(hook.callback));
});
// immediately resolve first hookChain
deferred.resolve();
// return the end of the hookChain
return hookChain;
}
function performParallel(parallelHooks) {
var deferred = $q.defer(),
promises = [];
// call each parallel event immediately
parallelHooks.forEach(function(hook) {
promises.push(hook.callback());
})
// resolve when all parallel hook promises are done
$q.all(promises).then(function() {
deferred.resolve();
})
.catch(function(error) {
deferred.reject(error);
})
return deferred.promise;
}
m.getHooksFor = function(event) {
var hooks = {
parallel: [],
series: []
}
tasks.forEach(function(task) {
if (task.event === event) {
if (task.parallel === true) {
hooks.parallel.push(task);
}
else {
hooks.series.push(task);
}
}
})
return hooks;
}
m.on = function(event, callback, parallel) {
tasks.push(new AsyncHook(event, callback, parallel));
}
m.once = function(event, callback, parallel) {
// TODO m.on + remove after called once
}
m.do = function(event) {
var hooks = m.getHooksFor(event),
deferred = $q.defer(),
promiseSeries = performSeries(hooks.series),
promiseParallel = performParallel(hooks.parallel);
// when parallel and series promises are complete, resolve the overall hook promise
$q.all([promiseSeries, promiseParallel]).then(function(a,b) {
deferred.resolve();
})
.catch(function(error) {
deferred.reject(error);
})
return deferred.promise;
}
// for use in "then" chains, simply wrap do in a callback
m.then = function(event) {
return function() {
return m.do(event);
}
}
return m;
});
})(window, window.angular);