Skip to content

Commit 90caa2b

Browse files
committed
Catch errors in async queue & re-start. Fixes #1933. Fixes #1759.
1 parent f62a80d commit 90caa2b

File tree

2 files changed

+76
-35
lines changed

2 files changed

+76
-35
lines changed

src/lib/async.html

+36-34
Original file line numberDiff line numberDiff line change
@@ -9,60 +9,62 @@
99
-->
1010
<script>
1111

12-
Polymer.Async = (function() {
13-
14-
var currVal = 0;
15-
var lastVal = 0;
16-
var callbacks = [];
17-
var twiddle = document.createTextNode('');
12+
Polymer.Async = {
1813

19-
function runAsync(callback, waitTime) {
14+
_currVal: 0,
15+
_lastVal: 0,
16+
_callbacks: [],
17+
_twiddleContent: 0,
18+
_twiddle: document.createTextNode(''),
19+
20+
run: function (callback, waitTime) {
2021
if (waitTime > 0) {
2122
return ~setTimeout(callback, waitTime);
2223
} else {
23-
twiddle.textContent = currVal++;
24-
callbacks.push(callback);
25-
return currVal - 1;
24+
this._twiddle.textContent = this._twiddleContent++;
25+
this._callbacks.push(callback);
26+
return this._currVal++;
2627
}
27-
}
28+
},
2829

29-
function cancelAsync(handle) {
30+
cancel: function(handle) {
3031
if (handle < 0) {
3132
clearTimeout(~handle);
3233
} else {
33-
var idx = handle - lastVal;
34+
var idx = handle - this._lastVal;
3435
if (idx >= 0) {
35-
if (!callbacks[idx]) {
36+
if (!this._callbacks[idx]) {
3637
throw 'invalid async handle: ' + handle;
3738
}
38-
callbacks[idx] = null;
39+
this._callbacks[idx] = null;
3940
}
4041
}
41-
}
42+
},
4243

43-
function atEndOfMicrotask() {
44-
var len = callbacks.length;
44+
_atEndOfMicrotask: function() {
45+
var len = this._callbacks.length;
4546
for (var i=0; i<len; i++) {
46-
var cb = callbacks[i];
47+
var cb = this._callbacks[i];
4748
if (cb) {
48-
cb();
49+
try {
50+
cb();
51+
} catch(e) {
52+
// Clear queue up to this point & start over after throwing
53+
i++;
54+
this._callbacks.splice(0, i);
55+
this._lastVal += i;
56+
this._twiddle.textContent = this._twiddleContent++;
57+
throw e;
58+
}
4959
}
5060
}
51-
callbacks.splice(0, len);
52-
lastVal += len;
61+
this._callbacks.splice(0, len);
62+
this._lastVal += len;
5363
}
64+
};
5465

55-
new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask)
56-
.observe(twiddle, {characterData: true})
57-
;
58-
59-
// exports
60-
61-
return {
62-
run: runAsync,
63-
cancel: cancelAsync
64-
};
65-
66-
})();
66+
new (window.MutationObserver || JsMutationObserver)
67+
(Polymer.Async._atEndOfMicrotask.bind(Polymer.Async))
68+
.observe(Polymer.Async._twiddle, {characterData: true});
6769

6870
</script>

test/unit/async.html

+40-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<body>
2020

2121
<script>
22-
22+
2323
setup(function() {
2424
window.Async = Polymer.Async;
2525
});
@@ -80,6 +80,45 @@
8080
});
8181
});
8282

83+
test('error handling', function(done) {
84+
var called1 = 0;
85+
var called2 = 0;
86+
var called3 = 0;
87+
var called4 = 0;
88+
var callback1 = function() {
89+
called1++;
90+
};
91+
var callback2 = function() {
92+
called2++;
93+
throw new Error('intentional error 2');
94+
};
95+
var callback3 = function() {
96+
called3++;
97+
throw new Error('intentional error 2');
98+
};
99+
var callback4 = function() {
100+
called4++;
101+
};
102+
Async.run(callback1);
103+
Async.run(callback2);
104+
Async.run(callback3);
105+
Async.run(callback4);
106+
// Force synchronous microtask, since we can't catch the error otherwise
107+
assert.throws(function(done) {
108+
Async._atEndOfMicrotask();
109+
});
110+
assert.throws(function(done) {
111+
Async._atEndOfMicrotask();
112+
});
113+
setTimeout(function() {
114+
assert.equal(called1, 1);
115+
assert.equal(called2, 1);
116+
assert.equal(called3, 1);
117+
assert.equal(called4, 1);
118+
done();
119+
});
120+
});
121+
83122
});
84123

85124
suite('cancel no-wait async', function() {

0 commit comments

Comments
 (0)