Merge branch 'dave-unclamp-safari' of https://github.com/dcsaszar/jasmine

* Significantly improves performance in Safari
* Merges #2040 from @dcsaszar
* Fixes #2008
This commit is contained in:
Steve Gravrock
2024-09-02 11:21:51 -07:00
3 changed files with 96 additions and 26 deletions

View File

@@ -2876,7 +2876,8 @@ getJasmineRequireObj().clearStack = function(j$) {
const maxInlineCallCount = 10; const maxInlineCallCount = 10;
function browserQueueMicrotaskImpl(global) { function browserQueueMicrotaskImpl(global) {
const { setTimeout, queueMicrotask } = global; const unclampedSetTimeout = getUnclampedSetTimeout(global);
const { queueMicrotask } = global;
let currentCallCount = 0; let currentCallCount = 0;
return function clearStack(fn) { return function clearStack(fn) {
currentCallCount++; currentCallCount++;
@@ -2885,7 +2886,7 @@ getJasmineRequireObj().clearStack = function(j$) {
queueMicrotask(fn); queueMicrotask(fn);
} else { } else {
currentCallCount = 0; currentCallCount = 0;
setTimeout(fn); unclampedSetTimeout(fn);
} }
}; };
} }
@@ -2899,6 +2900,35 @@ getJasmineRequireObj().clearStack = function(j$) {
} }
function messageChannelImpl(global) { function messageChannelImpl(global) {
const { setTimeout } = global;
const postMessage = getPostMessage(global);
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
if (currentCallCount < maxInlineCallCount) {
postMessage(fn);
} else {
currentCallCount = 0;
setTimeout(fn);
}
};
}
function getUnclampedSetTimeout(global) {
const { setTimeout } = global;
if (j$.util.isUndefined(global.MessageChannel)) return setTimeout;
const postMessage = getPostMessage(global);
return function unclampedSetTimeout(fn) {
postMessage(function() {
setTimeout(fn);
});
};
}
function getPostMessage(global) {
const { MessageChannel, setTimeout } = global; const { MessageChannel, setTimeout } = global;
const channel = new MessageChannel(); const channel = new MessageChannel();
let head = {}; let head = {};
@@ -2922,17 +2952,9 @@ getJasmineRequireObj().clearStack = function(j$) {
} }
}; };
let currentCallCount = 0; return function postMessage(fn) {
return function clearStack(fn) { tail = tail.next = { task: fn };
currentCallCount++; channel.port2.postMessage(0);
if (currentCallCount < maxInlineCallCount) {
tail = tail.next = { task: fn };
channel.port2.postMessage(0);
} else {
currentCallCount = 0;
setTimeout(fn);
}
}; };
} }

View File

@@ -20,6 +20,32 @@ describe('ClearStack', function() {
MessageChannel: fakeMessageChannel MessageChannel: fakeMessageChannel
}; };
}); });
it('uses MessageChannel to reduce setTimeout clamping', function() {
const fakeChannel = fakeMessageChannel();
spyOn(fakeChannel.port2, 'postMessage');
const queueMicrotask = jasmine.createSpy('queueMicrotask');
const global = {
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'
},
MessageChannel: function() {
return fakeChannel;
},
queueMicrotask
};
const clearStack = jasmineUnderTest.getClearStack(global);
for (let i = 0; i < 9; i++) clearStack(function() {});
expect(fakeChannel.port2.postMessage).not.toHaveBeenCalled();
clearStack(function() {});
expect(fakeChannel.port2.postMessage).toHaveBeenCalledTimes(1);
});
}); });
describe("in WebKit (Playwright's build for Windows)", function() { describe("in WebKit (Playwright's build for Windows)", function() {

View File

@@ -2,7 +2,8 @@ getJasmineRequireObj().clearStack = function(j$) {
const maxInlineCallCount = 10; const maxInlineCallCount = 10;
function browserQueueMicrotaskImpl(global) { function browserQueueMicrotaskImpl(global) {
const { setTimeout, queueMicrotask } = global; const unclampedSetTimeout = getUnclampedSetTimeout(global);
const { queueMicrotask } = global;
let currentCallCount = 0; let currentCallCount = 0;
return function clearStack(fn) { return function clearStack(fn) {
currentCallCount++; currentCallCount++;
@@ -11,7 +12,7 @@ getJasmineRequireObj().clearStack = function(j$) {
queueMicrotask(fn); queueMicrotask(fn);
} else { } else {
currentCallCount = 0; currentCallCount = 0;
setTimeout(fn); unclampedSetTimeout(fn);
} }
}; };
} }
@@ -25,6 +26,35 @@ getJasmineRequireObj().clearStack = function(j$) {
} }
function messageChannelImpl(global) { function messageChannelImpl(global) {
const { setTimeout } = global;
const postMessage = getPostMessage(global);
let currentCallCount = 0;
return function clearStack(fn) {
currentCallCount++;
if (currentCallCount < maxInlineCallCount) {
postMessage(fn);
} else {
currentCallCount = 0;
setTimeout(fn);
}
};
}
function getUnclampedSetTimeout(global) {
const { setTimeout } = global;
if (j$.util.isUndefined(global.MessageChannel)) return setTimeout;
const postMessage = getPostMessage(global);
return function unclampedSetTimeout(fn) {
postMessage(function() {
setTimeout(fn);
});
};
}
function getPostMessage(global) {
const { MessageChannel, setTimeout } = global; const { MessageChannel, setTimeout } = global;
const channel = new MessageChannel(); const channel = new MessageChannel();
let head = {}; let head = {};
@@ -48,17 +78,9 @@ getJasmineRequireObj().clearStack = function(j$) {
} }
}; };
let currentCallCount = 0; return function postMessage(fn) {
return function clearStack(fn) { tail = tail.next = { task: fn };
currentCallCount++; channel.port2.postMessage(0);
if (currentCallCount < maxInlineCallCount) {
tail = tail.next = { task: fn };
channel.port2.postMessage(0);
} else {
currentCallCount = 0;
setTimeout(fn);
}
}; };
} }