In Chrome I first create a continuous tone with the AudioContext:
var audioCtx = new (window.AudioContext || window.webkitAudioContext);
var oscillator = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.start();
Now I want to stop it after a few milliseconds. So I do this:
setTimeout(oscillator.stop, 500)
This returns the error Uncaught TypeError: Illegal invocation
.
However if I do;
setTimeout(function(){oscillator.stop()}, 500)
it works fine.
I would like to now why the first one doesn't work and returns an error. It seems like the straightforward way to do this.
In Chrome I first create a continuous tone with the AudioContext:
var audioCtx = new (window.AudioContext || window.webkitAudioContext);
var oscillator = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.start();
Now I want to stop it after a few milliseconds. So I do this:
setTimeout(oscillator.stop, 500)
This returns the error Uncaught TypeError: Illegal invocation
.
However if I do;
setTimeout(function(){oscillator.stop()}, 500)
it works fine.
I would like to now why the first one doesn't work and returns an error. It seems like the straightforward way to do this.
Share Improve this question asked Apr 15, 2015 at 2:55 HoushalterHoushalter 2,8181 gold badge19 silver badges21 bronze badges 4-
1
setTimeout(oscillator.stop.bind(oscillator), 500)
– elclanrs Commented Apr 15, 2015 at 2:59 - Thanks, that works. But why does it work, and why doesn't the simple way work? – Houshalter Commented Apr 15, 2015 at 3:07
-
This doesn't really answer your question, but another way to achieve the same effect would be to call
stop()
directly with the number of seconds to wait as the parameter so in your caseoscillator.stop(0.5)
. Here is a working fiddle – HJ05 Commented Apr 15, 2015 at 3:09 - I tried that first actually but it seems to be buggy in Chrome. It seems to measure the time since AudioContext was created, rather than the time since it was called. E.g. this: jsfiddle/j3q3npkc – Houshalter Commented Apr 15, 2015 at 3:29
1 Answer
Reset to default 9Your original code doesn't work because the stop
function is passed to setTimeout
without any context - it does not know which object it is supposed to act on. If you invoke it like this:
oscillator.stop();
Then within stop
, the special variable this
is set to the object pointed to by oscillator
. But if you refer to it like this:
var x = oscillator.stop;
The function is not actually invoked. Rather, a reference to the function is simply extracted from oscillator
and stored elsewhere. The function does not remember where it came from, and could be stored in many different variables or object properties at the same time. For example:
var x = {};
x.foo = oscillator.stop;
x.foo();
The last line calls stop
with a context of x
(this
is set to x
) rather than oscillator
. (The function's body will cause errors since stop
makes assumptions about what its context looks like, but the call itself is legal.) Alternatively, if you do this:
var foo = oscillator.stop;
foo();
Then stop
will be called with only a default context. In strict mode, this
will be set to undefined
, and in non-strict mode, this
will be set to window
.
When you do this:
setTimeout(function(){oscillator.stop()}, 500)
The anonymous function invokes stop
with the proper context. If, as suggested by @elclanrs in ments, you do this:
setTimeout(oscillator.stop.bind(oscillator), 500)
It is effectively the same thing: an anonymous function is created that calls stop
with a context of oscillator
.