Using setTimeout in a ServiceWorker - javascript

In my ServiceWorker I have the following code:
function send_alert() {
console.log('alert');
};
self.addEventListener('message', (evt) => {
console.log("message:" + evt.data);
self.setTimeout(1000*5, send_alert);
console.log("done");
});
If a button is clicked in the main page, a message is send to the worker. This works fine and I see the message: ... and done logs in the console. The self.setTimeout call does not raise an error, but send_alert is never called.
According to https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope implements WindowTimers which implements setTimeout. self should be an instance of ServiceWorkerGlobalScope, so I don't see the problem. What's the proper way to use setTimeout in a service worker?

Supposed the self.setTimeout function is the normal setTimeout function, then the order of the parameters is wrong: You first have to provide the callback, then the number of milliseconds.
This is, unfortunately, counterintuitive to what's seen as de-facto standard in JavaScript today, but setTimeout is pretty old, so… historical reasons 😉

Related

Can a web message call back function be executed before the previous is done executing?

If you have web messages which trigger a function and the messages are coming in quickly, can the function be executed before the previous is finished?
document.addEventListener('some_message', some_func, false);
function some_func (data){
//Is the code here always executed before the next function is executed?
}
Since there's only a single Javascript thread running, only one piece of code can run at a time. So this will always execute in the same order as events are triggered, and only one at a time:
function callback_func(data) {
alert(data);
}
However, if your callback works asynchronously, i.e. yields the thread back to the browser, then it's unpredictable:
function callback_func(data) {
setTimeout(() => {
// who knows when this will be called ¯\_(ツ)_/¯
alert(data);
}, Math.random());
}
Yes, that is the way asynchronous events work in JavaScript.
You can check out this article, which explains how async-await works in modern JavaScript.

Why will my subsequent Meteor method calls not wait for the first one to finish when I call Meteor.setTimeout()?

I am fairly new to Meteor, fibers and futures and I am trying to understand how Meteor methods work. It is my understanding that each method call from a client would wait for a previous one to finish. This belief is mostly based on the documentation of the this.unblock() function in the Meteor docs. However, when I try setting up a simple example with a Meteor.setTimeout() call this does not seem to be a correct assumption.
methodCall.js:
if (Meteor.isClient) {
Template.hello.events({
'click button': function () {
Meteor.call('test', function(error, result){
});
}
});
}
if (Meteor.isServer) {
Meteor.methods({
test: function(){
console.log("outside");
Meteor.setTimeout(function(){
console.log("inside");
return 'done';
}, 2000);
}
});
}
When triggering the 'click button' event several times the terminal output is as follows:
outside
outside
outside
outside
inside
inside
inside
inside
and not alternating between outside and inside as I would expect. I think there is a very relevant bit of information on Meteor.setTimeout() I am missing, but I could not find anything in the documentation indicating this behaviour. What am I missing and is there a way of making the Meteor method invocations from a client wait until a previous invocation is finished before starting the execution of the next?
I found this question on SO which seemed promising, but the question is more focused on blocking the possibility to call the method from the client side. Likewise, the accepted answer is not completely satisfying as it focuses on making subsequent calls skip certain code blocks of the Meteor method instead of waiting for the first invocation to finish. This very well be the answer I guess, but I really want to understand why the method call is not blocked in the first place as I feel the Meteor documentation indicates.
The answer is that the setTimeout callback is executed outside the fiber in which the method is running. What that means is that the method actually finishes execution (returning undefined) before the setTimeout callback is ever invoked, and you get the behavior you observed.
To provide a better test (and for an example of using asynchronous functions in methods), try this:
if (Meteor.isServer) {
var Future = Npm.require('fibers/future');
Meteor.methods({
test: function(){
var fut = new Future();
console.log("outside");
Meteor.setTimeout(function(){
console.log("inside");
fut.return('done');
return 'done';
}, 2000);
return fut.wait();
}
});
}
The return value from your setTimeout callback doesn't actually go anywhere, it just curtails that function (i.e. the callback, not the method). The way it's written above, the Future object, fut, is supplied with the return value once the callback runs, but the main method function (which is still running in its original fiber) is prevented from returning until that value has been supplied.
The upshot is that unless you unblock this method, you will get the expected output as the next method invocation won't start until the previous one has returned.
UPDATE
In general, anything with a callback will have the callback added to the event loop after the current Fiber is closed, so timeouts, HTTP calls, asynchronous DB queries - all of these fall into this category. If you want to recreate the environment of the method within the callback, you need to use Meteor.bindEnvironment otherwise you can't use any Meteor API functionality. This is an old, but very good video on the subject.

Open RequireJS Application with PhantomJS

I'm trying to load a single page application that uses a heavy amount of async code execution involving RequireJS and jQuery deferreds. The application loads as expected inside the browser, but not in PhantomJS.
For instance, I spent some time trying to figure out how to make the following snippet work:
# index.html
<body>
<script>
require.config({
base: '.',
paths: {
main: 'main'
}
})
require(['main'], function() {
window.myglobal = {
something: 'foo'
}
});
</script>
</body>
# phantomjs
page.evaluateAsync(function() {
console.log(window.myglobal.something); // Should print out 'foo'.
}, 100);
I consider that using evaluateAsync with a fixed timeout that has to be determined by trial and error is not really satisfactory. Perhaps someone can suggest a better pattern.
The documentation for evaluateAsync does not say much so I'm going to take it at face value and assume that it just executes the code asynchronously, without any further constraint regarding what may or may not have loaded already. (The source code does not indicate any further constraints either.)
The problem I'm seeing is that you have two asynchronous functions that may execute in any order. When require(['main'], ...) is called, this tells RequireJS to start loading the module but you don't know when the module will actually load. Similarly, when you execute page.evaluateAsync you are telling PhantomJS to execute a piece of code asynchronously. So it will execute but you don't know when.
So the following can happen:
The module finishes loading: window.myglobal is set.
console.log is called, which outputs the correct value.
Or:
console.log is called, which fails.
The module finishes loading: window.myglobal is set.
Setting a timeout that delays the execution of console.log will make it more likely that the first case happens but it does not guarantee it.
What you could do is change your HTML like this:
<body>
<script>
require.config({
base: '.',
paths: {
main: 'main'
}
})
define('init', ['main'], function () {
window.myglobal = {
something: 'foo'
};
});
require(['init']);
</script>
</body>
Your PhantomJS script:
page.evaluateAsync(function() {
require(['init'], function () {
console.log(window.myglobal.something); // Should print out 'foo'.
});
});
What this does is define a module called init. (This is a rare case where explicitly naming your module with define is okay. Usually you just start the define call with the list of dependencies.) Then when evaluateAsync is called it asks for the init module, which guarantees that the assignment to window.myglobal will have happened before console.log runs.
It would also be possible to use promises to get the desired results but I've preferred to show a solution that uses only RequireJS.
PhantomJS is a headless browser that is used for all kinds of stuff. A big part of it is the testing/automation of websites. It means that you generally don't have the opportunity of changing the site code. Most of the time it is not necessary, such as in this case.
You simply need to wait until the page script/DOM is at a state that you want for further processing. This is usually done using waitFor from the examples of PhantomJS.
In your case, you can add the waitFor definition to the beginning of the script and wait for window.myglobal to be defined:
page.open(url, function(){
waitFor(function check(){
return page.evaluate(function(){
return !!window.myglobal;
});
}, function then(){
// do something useful
}, 10000); // upper bound on acceptable wait timeout
});
check is a function which is called periodically to check that a certain condition is met. So the logic is that as soon as the condition is met, you can do something useful including doing something on the page using page.evaluate from the then callback.
There are also ways not to wait for specific variables/DOM nodes, but waiting for general ending of network activity as in this answer.

How to sleep in a firefox extension without using setTimeout?

I am trying to open a pop-up window, wait X seconds and then close the popup window.
(The use case is sending a notification to a webapp - but we can't just do a GET request as it needs to be in the same session so we can use the login session)
I can't use setTimeout as we can't use it in add-ons/extensions
How can I get similar functionality without resorting to chewing up CPU cycles, which obviously causes a noticeable lag?
You can use the timers module provided by the SDK instead of nsITimer for the same kind of setTimeout/setInterval functionality provided in browsers
let { setTimeout } = require('sdk/timers');
function openPopup () {}
setTimeout(openPopup, 3000);
You can use nsITimer.
A basic example is below but you can find more information (including the use of Components.interfaces.nsITimer.TYPE_REPEATING_SLACK as an alternative to setInterval) on the relevant documentation page at https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsITimer
// we need an nsITimerCallback compatible interface for the callbacks.
var event = {
notify: function(timer) {
alert("Fire!");
}
}
// Create the timer...
var timer = Components.classes["#mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
// initialize it to call event.notify() once after exactly ten seconds.
timer.initWithCallback(event,10000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);

Is it possible to stop JavaScript execution? [duplicate]

This question already has answers here:
How to terminate the script in JavaScript?
(25 answers)
Closed 7 years ago.
Is it possible in some way to stop or terminate JavaScript in a way that it prevents any further JavaScript-based execution from occuring, without reloading the browser?
I am thinking of a JavaScript equivalent of exit() in PHP.
Short answer:
throw new Error("Something went badly wrong!");
If you want to know more, keep reading.
Do you want to stop JavaScript's execution for developing/debugging?
The expression debugger; in your code, will halt the page execution, and then your browser's developer tools will allow you to review the state of your page at the moment it was frozen.
Do you want to stop your application arbitrarily and by design?
On error?
Instead of trying to stop everything, let your code handle the error. Read about Exceptions by googling. They are a smart way to let your code "jump" to error handling procedures without using tedious if/else blocks.
After reading about them, if you believe that interrupting the whole code is absolutely the only option, throwing an exception that is not going to be "caught" anywhere except in your application's "root" scope is the solution:
// creates a new exception type:
function FatalError(){ Error.apply(this, arguments); this.name = "FatalError"; }
FatalError.prototype = Object.create(Error.prototype);
// and then, use this to trigger the error:
throw new FatalError("Something went badly wrong!");
be sure you don't have catch() blocks that catch any exception; in this case modify them to rethrow your "FatalError" exception:
catch(exc){ if(exc instanceof FatalError) throw exc; else /* current code here */ }
When a task completes or an arbitrary event happens?
return; will terminate the current function's execution flow.
if(someEventHappened) return; // Will prevent subsequent code from being executed
alert("This alert will never be shown.");
Note: return; works only within a function.
In both cases...
...you may want to know how to stop asynchronous code as well. It's done with clearTimeout and clearInterval. Finally, to stop XHR (Ajax) requests, you can use the xhrObj.abort() method (which is available in jQuery as well).
You can make a JavaScript typo :D (thinking outside the box here)
thisFunctionDoesNotExistAndWasCreatedWithTheOnlyPurposeOfStopJavascriptExecutionOfAllTypesIncludingCatchAndAnyArbitraryWeirdScenario();
Or something like:
new new
Something like this might work:
function javascript_abort()
{
throw new Error('This is not an error. This is just to abort javascript');
}
Taken from here:
http://vikku.info/codesnippets/javascript/forcing-javascript-to-abort-stop-javascript-execution-at-any-time/
I do:
setTimeout(function() { debugger; }, 5000)
this way I have 5 seconds to interact with UI and then in stops. Las time I used was when I needed to leave custom tooltip visible, to do some styling changes.
No.
Even if you throw an exception, it will only kill the current event loop. Callbacks passed to setTimeout or DOM/XMLHttpRequest event handlers will still run when their time comes.
I am using
return false;
if I want to abort from JavaScript from running further downwards.
If you're in a function you can exit it using return; but that doesn't stop execution of the parent function that called that function.
You can call return early in a function, and at least that function will stop running. You can also just use throw '' to cause an error and stop the current process. But these won't stop everything. setTimeout and setInterval can make delayed functions and functions that run on a time interval, respectively. Those will continue to run. Javascript events will also continue to work as usual.
I know this is old, but I wanted to do this and I have found, in my opinion, a slightly improved solution of the throw answers. Just temporary supress the error messages and reactivate them later using setTimeout :
setTimeout(function() {
window.onerror = function(message, url, lineNumber) {
return false;
};
}, 50); // sets a slight delay and then restores normal error reporting
window.onerror = function(message, url, lineNumber) {
return true;
};
throw new Error('controlledError');
Define a variable inside the JavaScript function, set this variable to 1 if you want ot execute the function and set it to 0 if you want to stop it
var execute;
function do_something()
{
if (execute == 1)
{
// execute your function
}
else
{
// do nothing
}
}
The process is tedious, but in Firefox:
Open a blank tab/window to create a new environment for the script
from the current page
Populate that new environment with the script to execute
Activate the script in the new environment
Close (that is, kill) that new environment to ...
stop or terminate JavaScript this [in a] way to [that it] prevent[s] any further
JavaScript-based execution from occuring, without reloading the browser
Notes:
Step 4 only stops execution of JavaScript in that environment and not the scripts of any other windows
The original page is not reloaded but a new tab/window is loaded with the script
When a tab/window is closed, everything in that environment is gone: all remnants, partial results, code, etc.
Results must migrate back to the parent or another window for preservation
To rerun the code, the above steps must be repeated
Other browsers have and use different conventions.

Categories