Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 0adc036

Browse files
committed
fix(core): ensure that multiple requests to requestAnimationFrame are buffered
IE11 (and maybe some other browsers) do not optimize multiple calls to rAF. This code makes that happen internally within the $$rAF service before the next frame kicks in. Closes #11791
1 parent 1ee5861 commit 0adc036

File tree

2 files changed

+81
-3
lines changed

2 files changed

+81
-3
lines changed

src/ng/raf.js

+41-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function $$RAFProvider() { //rAF
1010
$window.webkitCancelRequestAnimationFrame;
1111

1212
var rafSupported = !!requestAnimationFrame;
13-
var raf = rafSupported
13+
var rafFn = rafSupported
1414
? function(fn) {
1515
var id = requestAnimationFrame(fn);
1616
return function() {
@@ -24,8 +24,46 @@ function $$RAFProvider() { //rAF
2424
};
2525
};
2626

27-
raf.supported = rafSupported;
27+
queueFn.supported = rafSupported;
2828

29-
return raf;
29+
var cancelLastRAF;
30+
var taskCount = 0;
31+
var taskQueue = [];
32+
return queueFn;
33+
34+
function flush() {
35+
for (var i = 0; i < taskQueue.length; i++) {
36+
var task = taskQueue[i];
37+
if (task) {
38+
taskQueue[i] = null;
39+
task();
40+
}
41+
}
42+
taskCount = taskQueue.length = 0;
43+
}
44+
45+
function queueFn(asyncFn) {
46+
var index = taskQueue.length;
47+
48+
taskCount++;
49+
taskQueue.push(asyncFn);
50+
51+
if (index === 0) {
52+
cancelLastRAF = rafFn(flush);
53+
}
54+
55+
return function cancelQueueFn() {
56+
if (index >= 0) {
57+
taskQueue[index] = null;
58+
index = null;
59+
60+
if (--taskCount === 0 && cancelLastRAF) {
61+
cancelLastRAF();
62+
cancelLastRAF = null;
63+
taskQueue.length = 0;
64+
}
65+
}
66+
};
67+
}
3068
}];
3169
}

test/ng/rafSpec.js

+40
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,46 @@ describe('$$rAF', function() {
3131
expect(present).toBe(true);
3232
}));
3333

34+
it('should only consume only one RAF if multiple async functions are registered before the first frame kicks in', inject(function($$rAF) {
35+
if (!$$rAF.supported) return;
36+
37+
//we need to create our own injector to work around the ngMock overrides
38+
var rafLog = [];
39+
var injector = createInjector(['ng', function($provide) {
40+
$provide.value('$window', {
41+
location: window.location,
42+
history: window.history,
43+
webkitRequestAnimationFrame: function(fn) {
44+
rafLog.push(fn);
45+
}
46+
});
47+
}]);
48+
49+
$$rAF = injector.get('$$rAF');
50+
51+
var log = [];
52+
function logFn() {
53+
log.push(log.length);
54+
}
55+
56+
$$rAF(logFn);
57+
$$rAF(logFn);
58+
$$rAF(logFn);
59+
60+
expect(log).toEqual([]);
61+
expect(rafLog.length).toBe(1);
62+
63+
rafLog[0]();
64+
65+
expect(log).toEqual([0,1,2]);
66+
expect(rafLog.length).toBe(1);
67+
68+
$$rAF(logFn);
69+
70+
expect(log).toEqual([0,1,2]);
71+
expect(rafLog.length).toBe(2);
72+
}));
73+
3474
describe('$timeout fallback', function() {
3575
it("it should use a $timeout incase native rAF isn't suppored", function() {
3676
var timeoutSpy = jasmine.createSpy('callback');

0 commit comments

Comments
 (0)