clearStack optimizations
* Avoid setTimeout in Node, because we don't need the overhead there. * Still call setTimeout in browsers to prevent the tab from being killed. * Use queueMicrotask in Safari, because it's dramatically faster than MessageChannel there. * Continue to use MessageChannel in other supported browsers becuase it's somewhat faster than queueMicrotask there. * Don't use setImmediate any more because there's a faster alternative in all supported envs. In jasmine-core's own test suite, this yields a roughly 50-70% speedup in Node, ~20% in Edge, and 75-90%(!) in Safari.
This commit is contained in:
@@ -2789,8 +2789,32 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
|||||||
getJasmineRequireObj().clearStack = function(j$) {
|
getJasmineRequireObj().clearStack = function(j$) {
|
||||||
const maxInlineCallCount = 10;
|
const maxInlineCallCount = 10;
|
||||||
|
|
||||||
function messageChannelImpl(global, setTimeout) {
|
function browserQueueMicrotaskImpl(global) {
|
||||||
const channel = new global.MessageChannel();
|
const { setTimeout, queueMicrotask } = global;
|
||||||
|
let currentCallCount = 0;
|
||||||
|
return function clearStack(fn) {
|
||||||
|
currentCallCount++;
|
||||||
|
|
||||||
|
if (currentCallCount < maxInlineCallCount) {
|
||||||
|
queueMicrotask(fn);
|
||||||
|
} else {
|
||||||
|
currentCallCount = 0;
|
||||||
|
setTimeout(fn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeQueueMicrotaskImpl(global) {
|
||||||
|
const { queueMicrotask } = global;
|
||||||
|
|
||||||
|
return function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function messageChannelImpl(global) {
|
||||||
|
const { MessageChannel, setTimeout } = global;
|
||||||
|
const channel = new MessageChannel();
|
||||||
let head = {};
|
let head = {};
|
||||||
let tail = head;
|
let tail = head;
|
||||||
|
|
||||||
@@ -2801,7 +2825,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
delete head.task;
|
delete head.task;
|
||||||
|
|
||||||
if (taskRunning) {
|
if (taskRunning) {
|
||||||
global.setTimeout(task, 0);
|
setTimeout(task, 0);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
taskRunning = true;
|
taskRunning = true;
|
||||||
@@ -2827,29 +2851,31 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getClearStack(global) {
|
function getClearStack(global) {
|
||||||
let currentCallCount = 0;
|
const NODE_JS =
|
||||||
const realSetTimeout = global.setTimeout;
|
global.process &&
|
||||||
const setTimeoutImpl = function clearStack(fn) {
|
global.process.versions &&
|
||||||
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
|
typeof global.process.versions.node === 'string';
|
||||||
};
|
|
||||||
|
|
||||||
if (j$.isFunction_(global.setImmediate)) {
|
const SAFARI =
|
||||||
const realSetImmediate = global.setImmediate;
|
global.navigator &&
|
||||||
return function(fn) {
|
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
|
||||||
currentCallCount++;
|
|
||||||
|
|
||||||
if (currentCallCount < maxInlineCallCount) {
|
if (NODE_JS) {
|
||||||
realSetImmediate(fn);
|
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||||
} else {
|
// so we avoid the overhead.
|
||||||
currentCallCount = 0;
|
return nodeQueueMicrotaskImpl(global);
|
||||||
|
} else if (
|
||||||
setTimeoutImpl(fn);
|
SAFARI ||
|
||||||
}
|
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||||
};
|
) {
|
||||||
} else if (!j$.util.isUndefined(global.MessageChannel)) {
|
// queueMicrotask is dramatically faster than MessageChannel in Safari.
|
||||||
return messageChannelImpl(global, setTimeoutImpl);
|
// Some of our own integration tests provide a mock queueMicrotask in all
|
||||||
|
// environments because it's simpler to mock than MessageChannel.
|
||||||
|
return browserQueueMicrotaskImpl(global);
|
||||||
} else {
|
} else {
|
||||||
return setTimeoutImpl;
|
// MessageChannel is faster than queueMicrotask in supported browsers
|
||||||
|
// other than Safari.
|
||||||
|
return messageChannelImpl(global);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,160 +9,216 @@ describe('ClearStack', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses setImmediate when available', function() {
|
describe('in Safari', function() {
|
||||||
const setImmediate = jasmine
|
usesQueueMicrotaskWithSetTimeout(function() {
|
||||||
.createSpy('setImmediate')
|
return {
|
||||||
.and.callFake(function(fn) {
|
navigator: {
|
||||||
fn();
|
userAgent:
|
||||||
}),
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.0.8 (KHTML, like Gecko) Version/15.1 Safari/605.0.8'
|
||||||
global = { setImmediate: setImmediate },
|
},
|
||||||
clearStack = jasmineUnderTest.getClearStack(global);
|
// queueMicrotask should be used even though MessageChannel is present
|
||||||
let called = false;
|
MessageChannel: fakeMessageChannel
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
clearStack(function() {
|
describe('in browsers other than Safari', function() {
|
||||||
called = true;
|
usesMessageChannel(function() {
|
||||||
|
return {
|
||||||
|
navigator: {
|
||||||
|
// Chrome's user agent string contains "Safari" so it's a good test case
|
||||||
|
userAgent:
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(called).toBe(true);
|
describe('when MessageChannel is unavailable', function() {
|
||||||
expect(setImmediate).toHaveBeenCalled();
|
usesQueueMicrotaskWithSetTimeout(function() {
|
||||||
|
return {
|
||||||
|
navigator: {
|
||||||
|
userAgent: 'CERN-LineMode/2.15 libwww/2.17b3',
|
||||||
|
MessageChannel: undefined
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses setTimeout instead of setImmediate every 10 calls to make sure we release the CPU', function() {
|
describe('in Node', function() {
|
||||||
const setImmediate = jasmine.createSpy('setImmediate'),
|
usesQueueMicrotaskWithoutSetTimeout(function() {
|
||||||
setTimeout = jasmine.createSpy('setTimeout'),
|
return {
|
||||||
global = { setImmediate: setImmediate, setTimeout: setTimeout },
|
process: {
|
||||||
clearStack = jasmineUnderTest.getClearStack(global);
|
versions: {
|
||||||
|
node: '3.1415927'
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
|
|
||||||
expect(setImmediate).toHaveBeenCalled();
|
|
||||||
expect(setTimeout).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
clearStack(function() {});
|
|
||||||
expect(setImmediate.calls.count()).toEqual(9);
|
|
||||||
expect(setTimeout.calls.count()).toEqual(1);
|
|
||||||
|
|
||||||
clearStack(function() {});
|
|
||||||
expect(setImmediate.calls.count()).toEqual(10);
|
|
||||||
expect(setTimeout.calls.count()).toEqual(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses MessageChannels when available', function() {
|
|
||||||
const fakeChannel = {
|
|
||||||
port1: {},
|
|
||||||
port2: {
|
|
||||||
postMessage: function() {
|
|
||||||
fakeChannel.port1.onmessage();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
global = {
|
});
|
||||||
MessageChannel: function() {
|
});
|
||||||
return fakeChannel;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clearStack = jasmineUnderTest.getClearStack(global);
|
|
||||||
let called = false;
|
|
||||||
|
|
||||||
clearStack(function() {
|
function usesMessageChannel(makeGlobal) {
|
||||||
called = true;
|
it('uses MessageChannel', function() {
|
||||||
|
const global = {
|
||||||
|
...makeGlobal(),
|
||||||
|
MessageChannel: fakeMessageChannel
|
||||||
|
};
|
||||||
|
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||||
|
let called = false;
|
||||||
|
|
||||||
|
clearStack(function() {
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(called).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(called).toBe(true);
|
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
|
||||||
});
|
const fakeChannel = fakeMessageChannel();
|
||||||
|
spyOn(fakeChannel.port2, 'postMessage');
|
||||||
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
|
const setTimeout = jasmine.createSpy('setTimeout');
|
||||||
const fakeChannel = {
|
const global = {
|
||||||
port1: {},
|
...makeGlobal(),
|
||||||
port2: {
|
setTimeout,
|
||||||
postMessage: jasmine
|
|
||||||
.createSpy('postMessage')
|
|
||||||
.and.callFake(function() {
|
|
||||||
fakeChannel.port1.onmessage();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setTimeout = jasmine.createSpy('setTimeout'),
|
|
||||||
global = {
|
|
||||||
MessageChannel: function() {
|
MessageChannel: function() {
|
||||||
return fakeChannel;
|
return fakeChannel;
|
||||||
},
|
|
||||||
setTimeout: setTimeout
|
|
||||||
},
|
|
||||||
clearStack = jasmineUnderTest.getClearStack(global);
|
|
||||||
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
clearStack(function() {});
|
|
||||||
|
|
||||||
expect(fakeChannel.port2.postMessage).toHaveBeenCalled();
|
|
||||||
expect(setTimeout).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
clearStack(function() {});
|
|
||||||
expect(fakeChannel.port2.postMessage.calls.count()).toEqual(9);
|
|
||||||
expect(setTimeout.calls.count()).toEqual(1);
|
|
||||||
|
|
||||||
clearStack(function() {});
|
|
||||||
expect(fakeChannel.port2.postMessage.calls.count()).toEqual(10);
|
|
||||||
expect(setTimeout.calls.count()).toEqual(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls setTimeout when onmessage is called recursively', function() {
|
|
||||||
const fakeChannel = {
|
|
||||||
port1: {},
|
|
||||||
port2: {
|
|
||||||
postMessage: function() {
|
|
||||||
fakeChannel.port1.onmessage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
setTimeout = jasmine.createSpy('setTimeout'),
|
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||||
global = {
|
|
||||||
MessageChannel: function() {
|
|
||||||
return fakeChannel;
|
|
||||||
},
|
|
||||||
setTimeout: setTimeout
|
|
||||||
},
|
|
||||||
clearStack = jasmineUnderTest.getClearStack(global),
|
|
||||||
fn = jasmine.createSpy('second clearStack function');
|
|
||||||
|
|
||||||
clearStack(function() {
|
for (let i = 0; i < 9; i++) {
|
||||||
clearStack(fn);
|
clearStack(function() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(fakeChannel.port2.postMessage).toHaveBeenCalled();
|
||||||
|
expect(setTimeout).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
clearStack(function() {});
|
||||||
|
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(9);
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
clearStack(function() {});
|
||||||
|
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(10);
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(fn).not.toHaveBeenCalled();
|
it('calls setTimeout when onmessage is called recursively', function() {
|
||||||
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
|
const setTimeout = jasmine.createSpy('setTimeout');
|
||||||
});
|
const global = {
|
||||||
|
...makeGlobal(),
|
||||||
|
setTimeout,
|
||||||
|
MessageChannel: fakeMessageChannel
|
||||||
|
};
|
||||||
|
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||||
|
const fn = jasmine.createSpy('second clearStack function');
|
||||||
|
|
||||||
it('falls back to setTimeout', function() {
|
clearStack(function() {
|
||||||
const setTimeout = jasmine
|
clearStack(fn);
|
||||||
.createSpy('setTimeout')
|
});
|
||||||
.and.callFake(function(fn) {
|
|
||||||
|
expect(fn).not.toHaveBeenCalled();
|
||||||
|
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function usesQueueMicrotaskWithSetTimeout(makeGlobal) {
|
||||||
|
it('uses queueMicrotask', function() {
|
||||||
|
const global = {
|
||||||
|
...makeGlobal(),
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
fn();
|
fn();
|
||||||
}),
|
}
|
||||||
global = { setTimeout: setTimeout },
|
};
|
||||||
clearStack = jasmineUnderTest.getClearStack(global);
|
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||||
let called = false;
|
let called = false;
|
||||||
|
|
||||||
clearStack(function() {
|
clearStack(function() {
|
||||||
called = true;
|
called = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(called).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(called).toBe(true);
|
it('uses setTimeout instead of queueMicrotask every 10 calls to make sure we release the CPU', function() {
|
||||||
expect(setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 0);
|
const queueMicrotask = jasmine.createSpy('queueMicrotask');
|
||||||
});
|
const setTimeout = jasmine.createSpy('setTimeout');
|
||||||
|
const global = {
|
||||||
|
...makeGlobal(),
|
||||||
|
queueMicrotask,
|
||||||
|
setTimeout
|
||||||
|
};
|
||||||
|
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||||
|
|
||||||
|
for (let i = 0; i < 9; i++) {
|
||||||
|
clearStack(function() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(queueMicrotask).toHaveBeenCalled();
|
||||||
|
expect(setTimeout).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
clearStack(function() {});
|
||||||
|
expect(queueMicrotask).toHaveBeenCalledTimes(9);
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
clearStack(function() {});
|
||||||
|
expect(queueMicrotask).toHaveBeenCalledTimes(10);
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function usesQueueMicrotaskWithoutSetTimeout(makeGlobal) {
|
||||||
|
it('uses queueMicrotask', function() {
|
||||||
|
const global = {
|
||||||
|
...makeGlobal(),
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||||
|
let called = false;
|
||||||
|
|
||||||
|
clearStack(function() {
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(called).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not use setTimeout', function() {
|
||||||
|
const queueMicrotask = jasmine.createSpy('queueMicrotask');
|
||||||
|
const setTimeout = jasmine.createSpy('setTimeout');
|
||||||
|
const global = {
|
||||||
|
...makeGlobal(),
|
||||||
|
queueMicrotask,
|
||||||
|
setTimeout
|
||||||
|
};
|
||||||
|
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||||
|
|
||||||
|
clearStack(function() {});
|
||||||
|
clearStack(function() {});
|
||||||
|
clearStack(function() {});
|
||||||
|
clearStack(function() {});
|
||||||
|
clearStack(function() {});
|
||||||
|
clearStack(function() {});
|
||||||
|
clearStack(function() {});
|
||||||
|
clearStack(function() {});
|
||||||
|
clearStack(function() {});
|
||||||
|
clearStack(function() {});
|
||||||
|
|
||||||
|
expect(queueMicrotask).toHaveBeenCalledTimes(10);
|
||||||
|
expect(setTimeout).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fakeMessageChannel() {
|
||||||
|
const channel = {
|
||||||
|
port1: {},
|
||||||
|
port2: {
|
||||||
|
postMessage: function() {
|
||||||
|
channel.port1.onmessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -436,6 +436,9 @@ describe('Env integration', function() {
|
|||||||
},
|
},
|
||||||
clearTimeout: function(fn, delay) {
|
clearTimeout: function(fn, delay) {
|
||||||
clearTimeout(fn, delay);
|
clearTimeout(fn, delay);
|
||||||
|
},
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||||
@@ -473,6 +476,9 @@ describe('Env integration', function() {
|
|||||||
},
|
},
|
||||||
clearTimeout: function(fn) {
|
clearTimeout: function(fn) {
|
||||||
clearTimeout(fn);
|
clearTimeout(fn);
|
||||||
|
},
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||||
@@ -529,6 +535,9 @@ describe('Env integration', function() {
|
|||||||
},
|
},
|
||||||
clearTimeout: function(fn, delay) {
|
clearTimeout: function(fn, delay) {
|
||||||
clearTimeout(fn, delay);
|
clearTimeout(fn, delay);
|
||||||
|
},
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||||
@@ -545,8 +554,8 @@ describe('Env integration', function() {
|
|||||||
env.it('fails', function(specDone) {
|
env.it('fails', function(specDone) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
specDone();
|
specDone();
|
||||||
setTimeout(function() {
|
queueMicrotask(function() {
|
||||||
setTimeout(function() {
|
queueMicrotask(function() {
|
||||||
global.onerror('fail');
|
global.onerror('fail');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -578,6 +587,9 @@ describe('Env integration', function() {
|
|||||||
},
|
},
|
||||||
clearTimeout: function(fn, delay) {
|
clearTimeout: function(fn, delay) {
|
||||||
clearTimeout(fn, delay);
|
clearTimeout(fn, delay);
|
||||||
|
},
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||||
@@ -638,6 +650,9 @@ describe('Env integration', function() {
|
|||||||
},
|
},
|
||||||
clearTimeout: function(fn, delay) {
|
clearTimeout: function(fn, delay) {
|
||||||
clearTimeout(fn, delay);
|
clearTimeout(fn, delay);
|
||||||
|
},
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||||
@@ -677,6 +692,9 @@ describe('Env integration', function() {
|
|||||||
},
|
},
|
||||||
clearTimeout: function(fn) {
|
clearTimeout: function(fn) {
|
||||||
clearTimeout(fn);
|
clearTimeout(fn);
|
||||||
|
},
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||||
@@ -1427,8 +1445,8 @@ describe('Env integration', function() {
|
|||||||
global: {
|
global: {
|
||||||
setTimeout: globalSetTimeout,
|
setTimeout: globalSetTimeout,
|
||||||
clearTimeout: clearTimeout,
|
clearTimeout: clearTimeout,
|
||||||
setImmediate: function(cb) {
|
queueMicrotask: function(fn) {
|
||||||
return setTimeout(cb, 0);
|
queueMicrotask(fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1501,8 +1519,8 @@ describe('Env integration', function() {
|
|||||||
clearTimeout: clearTimeout,
|
clearTimeout: clearTimeout,
|
||||||
setInterval: setInterval,
|
setInterval: setInterval,
|
||||||
clearInterval: clearInterval,
|
clearInterval: clearInterval,
|
||||||
setImmediate: function(cb) {
|
queueMicrotask: function(fn) {
|
||||||
return realSetTimeout(cb, 0);
|
queueMicrotask(fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -2625,6 +2643,9 @@ describe('Env integration', function() {
|
|||||||
clearTimeout: function(fn, delay) {
|
clearTimeout: function(fn, delay) {
|
||||||
clearTimeout(fn, delay);
|
clearTimeout(fn, delay);
|
||||||
},
|
},
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
|
},
|
||||||
onerror: function() {}
|
onerror: function() {}
|
||||||
};
|
};
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||||
@@ -2680,6 +2701,9 @@ describe('Env integration', function() {
|
|||||||
clearTimeout: function(fn, delay) {
|
clearTimeout: function(fn, delay) {
|
||||||
clearTimeout(fn, delay);
|
clearTimeout(fn, delay);
|
||||||
},
|
},
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
|
},
|
||||||
onerror: originalOnerror
|
onerror: originalOnerror
|
||||||
};
|
};
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||||
@@ -2869,6 +2893,9 @@ describe('Env integration', function() {
|
|||||||
},
|
},
|
||||||
clearTimeout: function(fn, delay) {
|
clearTimeout: function(fn, delay) {
|
||||||
return clearTimeout(fn, delay);
|
return clearTimeout(fn, delay);
|
||||||
|
},
|
||||||
|
queueMicrotask: function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||||
|
|||||||
@@ -1,8 +1,32 @@
|
|||||||
getJasmineRequireObj().clearStack = function(j$) {
|
getJasmineRequireObj().clearStack = function(j$) {
|
||||||
const maxInlineCallCount = 10;
|
const maxInlineCallCount = 10;
|
||||||
|
|
||||||
function messageChannelImpl(global, setTimeout) {
|
function browserQueueMicrotaskImpl(global) {
|
||||||
const channel = new global.MessageChannel();
|
const { setTimeout, queueMicrotask } = global;
|
||||||
|
let currentCallCount = 0;
|
||||||
|
return function clearStack(fn) {
|
||||||
|
currentCallCount++;
|
||||||
|
|
||||||
|
if (currentCallCount < maxInlineCallCount) {
|
||||||
|
queueMicrotask(fn);
|
||||||
|
} else {
|
||||||
|
currentCallCount = 0;
|
||||||
|
setTimeout(fn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeQueueMicrotaskImpl(global) {
|
||||||
|
const { queueMicrotask } = global;
|
||||||
|
|
||||||
|
return function(fn) {
|
||||||
|
queueMicrotask(fn);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function messageChannelImpl(global) {
|
||||||
|
const { MessageChannel, setTimeout } = global;
|
||||||
|
const channel = new MessageChannel();
|
||||||
let head = {};
|
let head = {};
|
||||||
let tail = head;
|
let tail = head;
|
||||||
|
|
||||||
@@ -13,7 +37,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
delete head.task;
|
delete head.task;
|
||||||
|
|
||||||
if (taskRunning) {
|
if (taskRunning) {
|
||||||
global.setTimeout(task, 0);
|
setTimeout(task, 0);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
taskRunning = true;
|
taskRunning = true;
|
||||||
@@ -39,29 +63,31 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getClearStack(global) {
|
function getClearStack(global) {
|
||||||
let currentCallCount = 0;
|
const NODE_JS =
|
||||||
const realSetTimeout = global.setTimeout;
|
global.process &&
|
||||||
const setTimeoutImpl = function clearStack(fn) {
|
global.process.versions &&
|
||||||
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
|
typeof global.process.versions.node === 'string';
|
||||||
};
|
|
||||||
|
|
||||||
if (j$.isFunction_(global.setImmediate)) {
|
const SAFARI =
|
||||||
const realSetImmediate = global.setImmediate;
|
global.navigator &&
|
||||||
return function(fn) {
|
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
|
||||||
currentCallCount++;
|
|
||||||
|
|
||||||
if (currentCallCount < maxInlineCallCount) {
|
if (NODE_JS) {
|
||||||
realSetImmediate(fn);
|
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||||
} else {
|
// so we avoid the overhead.
|
||||||
currentCallCount = 0;
|
return nodeQueueMicrotaskImpl(global);
|
||||||
|
} else if (
|
||||||
setTimeoutImpl(fn);
|
SAFARI ||
|
||||||
}
|
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||||
};
|
) {
|
||||||
} else if (!j$.util.isUndefined(global.MessageChannel)) {
|
// queueMicrotask is dramatically faster than MessageChannel in Safari.
|
||||||
return messageChannelImpl(global, setTimeoutImpl);
|
// Some of our own integration tests provide a mock queueMicrotask in all
|
||||||
|
// environments because it's simpler to mock than MessageChannel.
|
||||||
|
return browserQueueMicrotaskImpl(global);
|
||||||
} else {
|
} else {
|
||||||
return setTimeoutImpl;
|
// MessageChannel is faster than queueMicrotask in supported browsers
|
||||||
|
// other than Safari.
|
||||||
|
return messageChannelImpl(global);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user