-
Notifications
You must be signed in to change notification settings - Fork 410
Conversation
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed, please reply here (e.g.
|
1 similar comment
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed, please reply here (e.g.
|
I signed it! |
CLAs look good, thanks! |
1 similar comment
CLAs look good, thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the issue is not the handleId itself, I run v0.10, it seems the returned handleId is the zone patched version not the native version, in 0.10, the setTimeout look like this.
function () {
var t = NativeModule.require('timers');
return t.setTimeout.apply(this, arguments);
}
so I think we should not just check the handleId here is number or not, because it will not totally resolve the issue, it just like the error not thrown, we should find a way to let v0.10 return native version of timeout.
Basically you're right, of course. Simply suppressing errors is a bad idea in general. I came up with this idea for a node version of 'tasksByHandleId' (stripped js-version): function patchTimer(window, setName, cancelName, nameSuffix) {
var objectHandles = [];
var tasksByHandleId = {};
function scheduleTask(task) {
finally {
delete objectHandles[data.handleId];
delete tasksByHandleId[data.handleId];
}
}
if (objectHandles.length > 3000000) {
var newObjectHandles = [];
var newTasksByHandleId = {};
for (var i in tasksByHandleId) {
tasksByHandleId[i].data.handleId = newObjectHandles.length;
newObjectHandles[tasksByHandleId[i].data.handleId] = objectHandles[i];
newTasksByHandleId[tasksByHandleId[i].data.handleId] = tasksByHandleId[i];
}
objectHandles = newObjectHandles;
tasksByHandleId = newTasksByHandleId;
}
data.handleId = objectHandles.length;
objectHandles[data.handleId] = setNative.apply(window, data.args);
tasksByHandleId[data.handleId] = task;
}
function clearTask(task) {
var objectHandle = objectHandles[task.data.handleId];
delete objectHandles[task.data.handleId];
delete tasksByHandleId[task.data.handleId];
return clearNative(objectHandle);
} It puts the objects in an array, uses the index as handle id, and does the laundry now and then. I decided not to pursue it further, because apart from What do you think? |
@manuelschneider , I think the reason of the issue is in nodejs v0.10, nodejs lazy load timers, so global.setTimeout !== timers.setTimeout, and zone.js patch them all. I think we just remove global patch is ok, but I think it will need some test. in node.ts, currently the logic is // Timers
const timers = require('timers');
patchTimer(timers, set, clear, 'Timeout');
patchTimer(timers, set, clear, 'Interval');
patchTimer(timers, set, clear, 'Immediate');
const shouldPatchGlobalTimers = global.setTimeout !== timers.setTimeout;
if (shouldPatchGlobalTimers) {
patchTimer(_global, set, clear, 'Timeout');
patchTimer(_global, set, clear, 'Interval');
patchTimer(_global, set, clear, 'Immediate');
} I think we can change it to // Timers
const timers = require('timers');
patchTimer(timers, set, clear, 'Timeout');
patchTimer(timers, set, clear, 'Interval');
patchTimer(timers, set, clear, 'Immediate'); it will be ok, I think, but we should add some test and findout why we patch global before. |
When you skip the global patches, the existing tests fail at
If you remove the timers patch instead, at least the existing tests are ok and it seem to work. However, I still don't understand, why this is related to the exceptions? From what I understand, they're simply a |
@manuelschneider , the test code failed is reasonable, if we modify that way, we will modify the test case as well.
Yeah, I don't know either. I will try to find out why.
I believe that in nodejs v0.10, the timer.setTimeout still return an integer, not object, the reason it returns object because it return a ZoneTask(which is patched by zone.js), so it will cause the error. So I think the real solution should let setTimeout still return integer correctly. We need to add some tests to simulate the nodejs v0.10 behavior. |
I don't think so:
|
@manuelschneider , yes, you are right, in v0.10, it return a TimerObject, and it doesn't have any id like property. So I think your initial solution is ok because setTimeout really return a TimerObject not an integer, I think you just need to add test code to simulate such case to complete this PR. |
How do I run tests with node v0.10?
|
@manuelschneider , I think you just need to add a test case in test/common/setTimeout.spec.ts, and write a fake setTimeout which return a fake timerObject to override global setTimeout and test if setTimeout and clearTimeout work and recover the setTimeout to original one after your test. |
I don't get it. You want me to patch the patched setTimeout with a fake one? Why? The existing testcases test if a timeout can be scheduled and canceled. In my opinion that's exactly what is needed to make sure the changes don't break something. My change should not have modified previously existing behaviour. It simply reduced the usage of
=> Objects still get stringified in newer node versions, when used as key. Which makes them As far as I understand it, the patched setTimeout always returns a task. But when I don't now, but for now I can't think of a useful test to my patch apart from the already existing ones. |
@manuelschneider , in current cases, it can only make sure the changes not break anything, I mean we should simulate node v0.10 behavior to test if global.setTimeout return a object insteadof integer, our logic should also work. the case should like const originalSetTimeout = global.setTimeout;
global.setTimeout = function() {
const originalResult = originalSetTimeout.apply(this, arguments);
const fakeResult = {
timer: originalResult;
}
return fakeResult;
}
const originalClearTimeout = global.clearTimeout;
global.clearTimeout = function(timerObject) {
return originalClearTimeout.apply(this, [timerObject.timer]);
}
Zone.current.fork({
name: 'test',
scheduleTask: (...) => {
...
// some spy here
},
cancelTask:(...) => {
// some spy here
}
}).run(() => {
const timerId = setTimeout(() => {
// some other spy here
}, 100);
clearTimeout(timerId);
});
expect(scheduleSpy).toHaveBeenCalled();
expect(cancelSpy).toHaveBeenCalled();
expect(timeoutCallbackSpy).toHaveBeenCalled();
global.setTimeout = originalSetTimeout;
global.clearTimeout = originalClearTimeout; |
I still don't see how this should test handling the exceptions? |
@manuelschneider , in my understandings, we should test from those 2 views.
What do you think? |
This is still the case in newer node versions:
See also current API. Therefore in my opinion this is already covered by running the current testcases in node (doesn't matter what version). The exception-problem however is not testable without node 0.10, because |
@manuelschneider , sorry, I think I misunderstand this issue totally, I will check it. |
@manuelschneider , I think you are right. We don't need to add test cases. Node always return complex Timer Object. So the taskHandleId object should only hold integer type of timer id. So in fact we have 2 problems
So this fix in fact not only resolve the nodejs v0.10 issue, but also remove the unused process for saving an complex object.toString() into taskHandleId object and never use it. |
Thank you very much! I'll try to look into the 'patch-problem' you stated above within the next days. Patching only the global setTimeout seems to have some performance benefit in node v0.10. If I manage to figure out what's wrong there I'll submit another issue/merge request. |
Older node versions (0.10, still default in debian stable) will throw exceptions while trying to use the objects returned nodes Timers implementation as keys -> don't make them try it.
For the same reason - as far as I understand it - this has never worked in node, also newer versions, anyway. Therefore this shouldn't break anything.