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$) {
|
||||
const maxInlineCallCount = 10;
|
||||
|
||||
function messageChannelImpl(global, setTimeout) {
|
||||
const channel = new global.MessageChannel();
|
||||
function browserQueueMicrotaskImpl(global) {
|
||||
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 tail = head;
|
||||
|
||||
@@ -2801,7 +2825,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
delete head.task;
|
||||
|
||||
if (taskRunning) {
|
||||
global.setTimeout(task, 0);
|
||||
setTimeout(task, 0);
|
||||
} else {
|
||||
try {
|
||||
taskRunning = true;
|
||||
@@ -2827,29 +2851,31 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
}
|
||||
|
||||
function getClearStack(global) {
|
||||
let currentCallCount = 0;
|
||||
const realSetTimeout = global.setTimeout;
|
||||
const setTimeoutImpl = function clearStack(fn) {
|
||||
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
|
||||
};
|
||||
const NODE_JS =
|
||||
global.process &&
|
||||
global.process.versions &&
|
||||
typeof global.process.versions.node === 'string';
|
||||
|
||||
if (j$.isFunction_(global.setImmediate)) {
|
||||
const realSetImmediate = global.setImmediate;
|
||||
return function(fn) {
|
||||
currentCallCount++;
|
||||
const SAFARI =
|
||||
global.navigator &&
|
||||
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
|
||||
|
||||
if (currentCallCount < maxInlineCallCount) {
|
||||
realSetImmediate(fn);
|
||||
} else {
|
||||
currentCallCount = 0;
|
||||
|
||||
setTimeoutImpl(fn);
|
||||
}
|
||||
};
|
||||
} else if (!j$.util.isUndefined(global.MessageChannel)) {
|
||||
return messageChannelImpl(global, setTimeoutImpl);
|
||||
if (NODE_JS) {
|
||||
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||
// so we avoid the overhead.
|
||||
return nodeQueueMicrotaskImpl(global);
|
||||
} else if (
|
||||
SAFARI ||
|
||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||
) {
|
||||
// queueMicrotask is dramatically faster than MessageChannel in Safari.
|
||||
// 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 {
|
||||
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() {
|
||||
const setImmediate = jasmine
|
||||
.createSpy('setImmediate')
|
||||
.and.callFake(function(fn) {
|
||||
fn();
|
||||
}),
|
||||
global = { setImmediate: setImmediate },
|
||||
clearStack = jasmineUnderTest.getClearStack(global);
|
||||
let called = false;
|
||||
describe('in Safari', function() {
|
||||
usesQueueMicrotaskWithSetTimeout(function() {
|
||||
return {
|
||||
navigator: {
|
||||
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'
|
||||
},
|
||||
// queueMicrotask should be used even though MessageChannel is present
|
||||
MessageChannel: fakeMessageChannel
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
describe('in browsers other than Safari', function() {
|
||||
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);
|
||||
expect(setImmediate).toHaveBeenCalled();
|
||||
describe('when MessageChannel is unavailable', function() {
|
||||
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() {
|
||||
const setImmediate = jasmine.createSpy('setImmediate'),
|
||||
setTimeout = jasmine.createSpy('setTimeout'),
|
||||
global = { setImmediate: setImmediate, 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(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();
|
||||
describe('in Node', function() {
|
||||
usesQueueMicrotaskWithoutSetTimeout(function() {
|
||||
return {
|
||||
process: {
|
||||
versions: {
|
||||
node: '3.1415927'
|
||||
}
|
||||
}
|
||||
},
|
||||
global = {
|
||||
MessageChannel: function() {
|
||||
return fakeChannel;
|
||||
}
|
||||
},
|
||||
clearStack = jasmineUnderTest.getClearStack(global);
|
||||
let called = false;
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
function usesMessageChannel(makeGlobal) {
|
||||
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 = {
|
||||
port1: {},
|
||||
port2: {
|
||||
postMessage: jasmine
|
||||
.createSpy('postMessage')
|
||||
.and.callFake(function() {
|
||||
fakeChannel.port1.onmessage();
|
||||
})
|
||||
}
|
||||
},
|
||||
setTimeout = jasmine.createSpy('setTimeout'),
|
||||
global = {
|
||||
it('uses setTimeout instead of MessageChannel every 10 calls to make sure we release the CPU', function() {
|
||||
const fakeChannel = fakeMessageChannel();
|
||||
spyOn(fakeChannel.port2, 'postMessage');
|
||||
const setTimeout = jasmine.createSpy('setTimeout');
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
setTimeout,
|
||||
MessageChannel: function() {
|
||||
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'),
|
||||
global = {
|
||||
MessageChannel: function() {
|
||||
return fakeChannel;
|
||||
},
|
||||
setTimeout: setTimeout
|
||||
},
|
||||
clearStack = jasmineUnderTest.getClearStack(global),
|
||||
fn = jasmine.createSpy('second clearStack function');
|
||||
};
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
|
||||
clearStack(function() {
|
||||
clearStack(fn);
|
||||
for (let i = 0; i < 9; i++) {
|
||||
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();
|
||||
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
|
||||
});
|
||||
it('calls setTimeout when onmessage is called recursively', function() {
|
||||
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() {
|
||||
const setTimeout = jasmine
|
||||
.createSpy('setTimeout')
|
||||
.and.callFake(function(fn) {
|
||||
clearStack(function() {
|
||||
clearStack(fn);
|
||||
});
|
||||
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
expect(setTimeout).toHaveBeenCalledWith(fn, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function usesQueueMicrotaskWithSetTimeout(makeGlobal) {
|
||||
it('uses queueMicrotask', function() {
|
||||
const global = {
|
||||
...makeGlobal(),
|
||||
queueMicrotask: function(fn) {
|
||||
fn();
|
||||
}),
|
||||
global = { setTimeout: setTimeout },
|
||||
clearStack = jasmineUnderTest.getClearStack(global);
|
||||
let called = false;
|
||||
}
|
||||
};
|
||||
const clearStack = jasmineUnderTest.getClearStack(global);
|
||||
let called = false;
|
||||
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
clearStack(function() {
|
||||
called = true;
|
||||
});
|
||||
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
|
||||
expect(called).toBe(true);
|
||||
expect(setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 0);
|
||||
});
|
||||
it('uses setTimeout instead of queueMicrotask every 10 calls to make sure we release the CPU', function() {
|
||||
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(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
@@ -473,6 +476,9 @@ describe('Env integration', function() {
|
||||
},
|
||||
clearTimeout: function(fn) {
|
||||
clearTimeout(fn);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
@@ -529,6 +535,9 @@ describe('Env integration', function() {
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
@@ -545,8 +554,8 @@ describe('Env integration', function() {
|
||||
env.it('fails', function(specDone) {
|
||||
setTimeout(function() {
|
||||
specDone();
|
||||
setTimeout(function() {
|
||||
setTimeout(function() {
|
||||
queueMicrotask(function() {
|
||||
queueMicrotask(function() {
|
||||
global.onerror('fail');
|
||||
});
|
||||
});
|
||||
@@ -578,6 +587,9 @@ describe('Env integration', function() {
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
@@ -638,6 +650,9 @@ describe('Env integration', function() {
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
@@ -677,6 +692,9 @@ describe('Env integration', function() {
|
||||
},
|
||||
clearTimeout: function(fn) {
|
||||
clearTimeout(fn);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
@@ -1427,8 +1445,8 @@ describe('Env integration', function() {
|
||||
global: {
|
||||
setTimeout: globalSetTimeout,
|
||||
clearTimeout: clearTimeout,
|
||||
setImmediate: function(cb) {
|
||||
return setTimeout(cb, 0);
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1501,8 +1519,8 @@ describe('Env integration', function() {
|
||||
clearTimeout: clearTimeout,
|
||||
setInterval: setInterval,
|
||||
clearInterval: clearInterval,
|
||||
setImmediate: function(cb) {
|
||||
return realSetTimeout(cb, 0);
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2625,6 +2643,9 @@ describe('Env integration', function() {
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
},
|
||||
onerror: function() {}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
@@ -2680,6 +2701,9 @@ describe('Env integration', function() {
|
||||
clearTimeout: function(fn, delay) {
|
||||
clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
},
|
||||
onerror: originalOnerror
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
@@ -2869,6 +2893,9 @@ describe('Env integration', function() {
|
||||
},
|
||||
clearTimeout: function(fn, delay) {
|
||||
return clearTimeout(fn, delay);
|
||||
},
|
||||
queueMicrotask: function(fn) {
|
||||
queueMicrotask(fn);
|
||||
}
|
||||
};
|
||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
||||
|
||||
@@ -1,8 +1,32 @@
|
||||
getJasmineRequireObj().clearStack = function(j$) {
|
||||
const maxInlineCallCount = 10;
|
||||
|
||||
function messageChannelImpl(global, setTimeout) {
|
||||
const channel = new global.MessageChannel();
|
||||
function browserQueueMicrotaskImpl(global) {
|
||||
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 tail = head;
|
||||
|
||||
@@ -13,7 +37,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
delete head.task;
|
||||
|
||||
if (taskRunning) {
|
||||
global.setTimeout(task, 0);
|
||||
setTimeout(task, 0);
|
||||
} else {
|
||||
try {
|
||||
taskRunning = true;
|
||||
@@ -39,29 +63,31 @@ getJasmineRequireObj().clearStack = function(j$) {
|
||||
}
|
||||
|
||||
function getClearStack(global) {
|
||||
let currentCallCount = 0;
|
||||
const realSetTimeout = global.setTimeout;
|
||||
const setTimeoutImpl = function clearStack(fn) {
|
||||
Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
|
||||
};
|
||||
const NODE_JS =
|
||||
global.process &&
|
||||
global.process.versions &&
|
||||
typeof global.process.versions.node === 'string';
|
||||
|
||||
if (j$.isFunction_(global.setImmediate)) {
|
||||
const realSetImmediate = global.setImmediate;
|
||||
return function(fn) {
|
||||
currentCallCount++;
|
||||
const SAFARI =
|
||||
global.navigator &&
|
||||
/^((?!chrome|android).)*safari/i.test(global.navigator.userAgent);
|
||||
|
||||
if (currentCallCount < maxInlineCallCount) {
|
||||
realSetImmediate(fn);
|
||||
} else {
|
||||
currentCallCount = 0;
|
||||
|
||||
setTimeoutImpl(fn);
|
||||
}
|
||||
};
|
||||
} else if (!j$.util.isUndefined(global.MessageChannel)) {
|
||||
return messageChannelImpl(global, setTimeoutImpl);
|
||||
if (NODE_JS) {
|
||||
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||
// so we avoid the overhead.
|
||||
return nodeQueueMicrotaskImpl(global);
|
||||
} else if (
|
||||
SAFARI ||
|
||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
||||
) {
|
||||
// queueMicrotask is dramatically faster than MessageChannel in Safari.
|
||||
// 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 {
|
||||
return setTimeoutImpl;
|
||||
// MessageChannel is faster than queueMicrotask in supported browsers
|
||||
// other than Safari.
|
||||
return messageChannelImpl(global);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user