I have that line of codes
...
$timeout(tempFunc, $scope.sync.getDelay());
...
at my temp function I have that line of code at end:
$scope.sync.releasePrivilege();
and everything works well. However when I try:
...
$timeout(tempFunc, $scope.sync.getDelay());
$scope.sync.releasePrivilege();
...
It doesn't. I think that I should write that line as a callback function into timeout. I don't want to change recent functions at my code I can just edit that lines.
Any ideas?
PS: The problem is that:
$scope.sync.releasePrivilege();
is not running after timeout, it immediately runs.
$timeout is a wrapper for setTimeout that gets mocked out during testing. #MarkRajcok is completely right about about why using it as a blocking method doesn't work. Mark's solution would also solve your issue. But if it's not feasible to relocate your code, there is still good news!
$timeout returns a promise (see $q), so you can actually just chain together what you want:
$timeout( tempFunc, $scope.sync.getDelay() ).then( function() {
console.log("I'm called only after the timeout.");
$scope.sync.releasePrivilege();
});
console.log("But I get called immediately.");
And this should work just fine, should you fancy. It still doesn't block. It just ensures that the function within the then call is executed only after the promise is resolved, that is only when the timeout has completed and your method has been called.
Additionally, your function can return data, if needed. So if tempFunc returned a Boolean value that indicated success, you could also access it:
$timeout( tempFunc, $scope.sync.getDelay() ).then( function( result ) {
if ( result ) {
$scope.sync.releasePrivilege();
} else {
// handle the error
}
});
And there was much rejoicing. Yay.
Just as a note: doing a sleep in a browser would be very bad - it'd lock the UI. Asynchronous execution is what makes the web an awesome platform!
Timeout does not provide the equivalent of a "sleep". $timeout puts work (in your case, tempFunc) on the native event queue, and hence tempFunc will be called later (after the browser renders). $scope.sync.releasePrivilege(); will therefore be executed before tempFunc. As you stated, if you want releasePrivilege() to execute after tempFunc(), have tempFunc() call it.
Related
I want to add colspan with jQuery to an element. If I insert this function into console in Chrome then it works fine, but if I put it in document ready and run it in the script then nothing happens. This function should run after all scripts have run if possible. Any tips will help, no access to HTML.
$( document ).ready(function() {
$("#fieldset_Q8aGrid td:eq(4)").attr('colSpan',3)
});
You probably have some asynchronous scripts running which add the content after your code runs. If you do not have access to that other code, then the most pragmatic solution is to try from time to time until it succeeds:
$( document ).ready(function() {
var timer = setInterval(function () {
if ($("#fieldset_Q8aGrid td:eq(4)").attr('colSpan',3).length) {
clearInterval(timer);
}
}, 500); // check every half-second.
});
I am assuming you have some async code in your program that, for example, create dynamic HTML. This is the case if you don't know when all the functions will completely execute.
We can split this problem into two parts.
If you call a function X after a sync code Y, X will happen after Y.
But if you have call a function X after an async code Y, you don't know what will execute first.
To solve 1) you just have to put your code at the end of your script.
To solve 2) you have to use jQuery's deferred and when to explicitly tell your program: execute this when X, Y, Z is done.
Quick explanation (and quite not precise) of what a promise is:
A promise is an object with a then method (thenable). The then method
can receive a function that will be called when the promise is
resolved
From an animation you can use the .promise() method to create a promise that will be resolved when the animation is done.
var animationPromise = $('h1')
.css({'left': '0', 'position' : 'relative'})
.animate({'left': '20px'}, 700, 'linear')
.promise()
If you are making a call to a service, the jQuery's post, get and ajax, return a promise
// save the promise for later use
var asyncPromise = $.get('https://jsonplaceholder.typicode.com/users');
// execute some the function that uses this data
asyncPromise.then(function(users) { /* do something with the data */ })
Maybe, you have some functionality that is neither an AJAX or an animation, in this case you can create your own promise. For example, this promise is resolved from a setTimeout.
var backgroundChange = $.Deferred()
setTimeout(function() {
$('body').css('background', 'lightblue')
backgroundChange.resolve(true)
}, 3000) /* Please note that backgroundChange
doesn't know that it will be resolved after 3 seconds */
Later on, you can use .when(...) to create a promise that is resolved when a set of promises are resolved.
$.when(asyncPromise, backgroundChange, animationPromise)
.then(function() {
$('.container').css('color', 'blue')
//alert('Everything is done!');
})
Here is a working example that solves the two problems https://codepen.io/anon/pen/JWBXLd?editors=1010
You can either rearrange your scripts to change their order, or go with window.load:
$(window).load(function() {
// code
});
But this will fire after literally all your page is loaded. Even images. So it'll be better to go with the first approach.
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.
I'm trying to implement this kind of logic in Javascript:
LOOP
doStuff();
END
console.log("Stuff has been done");
I've managed to do it this way:
var loop = function() {
console.log("events");
window.requestAnimationFrame(loop);
}
window.requestAnimationFrame(loop);
console.log("loop is finished");
someOtherCodeGoesHere();
But it doesn't work. Well, it does, but "loop is finished" appears even before RAF is called. This whole code makes sense though, but it's not working as I want it to.
I've also figured out that I can make loop() return a callback function once a condition is met, but I don't want to enclose someOtherCodeGoesHere(); inside it because it's not what I want. Let's say if I have 10 loops, I'd have a callback hell. I just want it to keep going with the code flow, like a plain GOTO if you will.
Any ideas are welcome! :)
I am not familiar with the window.requestAnimationFrame() method, but since you are passing a callback as its only parameter I assume it is an asynchronous function. This means that its invocation does not block the execution of the methods after it. There is no guarantee that the RAF method will call the callback you passed in before the console.log is called. Normally any logic you want to happen after an async method runs you should put in the callback. Dealing with callbacks can be a pain so libraries like async can be a great help. Here is an example of how you might write this using the async lib. (This code is assuming you wanted an infinite loop due to the recursion your attempting in your code)
var loop = function() {
window.requestAnimationFrame(function() {
console.log("events");
});
}
async.forever(loop, function(err) {
console.log(err); // Only gets called if an error occurs
});
To speed up my application I want to prepare some data before DOM is ready and then use this data when DOM is ready.
Here's how it might be:
var data = function prepareData(){
...
}();
$(document).ready(function() {
// use data to build page
}
How to prepare the data for later use?
Thanks
You need should use parentheses around the function expression for clarity (and because in a similar situation where you're defining and calling a function but not using the return value, it would be a syntax error without them). Also, when you use a function expression, you want to not give it a name. So:
var data = (function(){
...
})();
or use a function declaration instead:
var data = processData();
function processData() {
...
}
(Why not use a name with a function expression? Because of bugs in various implementations, especially Internet Explorer prior to IE9, which will create two completely unrelated functions.)
However, it's not clear to me what you're trying to achieve. When the browser reaches the script element, it hands off to the JavaScript interpreter and waits for it to finish before continuing building the DOM (because your script might use document.write to add to the HTML token stream). You can use the async or defer attributes to promise the browser you're not going to use document.write, on browsers that support them, but...
Update: Below you've said:
because prepareData is long time function and I assumed that browser can execute this while it's building DOM tree. Unfortunately '$(document).ready' fires before prepareData is finished. The question is how to teach '$(document).ready' to wait for ready data
The only way the ready handler can possibly trigger while processData is running is if processData is using asynchronous ajax (or a couple of edge conditions around alert, confirm, and the like, but I assume you're not doing that). And if it were, you couldn't be returning the result as a return value from the function (though you could return an object that you continued to update as the result of ajax callbacks). Otherwise, it's impossible: JavaScript on browsers is single-threaded, the ready handler will queue waiting for the interpreter to finish its previous task (processData).
If processData isn't doing anything asynchronous, I suspect whatever the symptom is that you're seeing making you think the ready handler is firing during processData has a different cause.
But in the case of asynchronous stuff, three options:
If you're not in control of the ready handlers you want to hold up, you might look at jQuery's holdReady feature. Call $.holdReady(true); to hold up the event, and use $.holdReady(false); to stop holding it up.
It's simple enough to reschedule the ready handler. Here's how I'd do it (note that I've wrapped everything in a scoping function so these things aren't globals):
(function() {
var data = processData();
$(onPageReady);
function processData() {
}
function onPageReady() {
if (!data.ready) {
// Wait for it to be ready
setTimeout(onPageReady, 0); // 0 = As soon as possible, you may want a
// longer delay depending on what `processData`
// is waiting for
return;
}
}
})();
Note that I happily use data in the onPageReady function, because I know that it's there; that function will not run until processData has returned. But I'm assuming processData is returning an object that is slowly being filled in via ajax calls, so I've used a ready flag on the object that will get set when all the data is ready.
If you can change processData, there's a better solution: Have processData trigger the ready handler when it's done. Here's the code for when processData is done with what it needs to do:
$(onPageReady);
That works because if the DOM isn't ready yet, that just schedules the call. If the DOM is already ready, jQuery will call your function immediately. This prevents the messy looping above.
Why there no such function in javascript that sets a timeout for its continuation, saves the necessary state (the scope object and the execution point), terminates the script and gives the control back to the browser? After the timeout expires the browser would load back the execution context and continues the script, and we would have a real non browser blocking sleep functionality that would work even if the JS engine is single threaded.
Why there is still no such functionality in javascript? Why do we have to still slice our code into functions and set the timeouts to the next step to achieve the sleep effect?
I think 'sleep'ing is something you do not want in your browser.
First of all it might be not clear what has to happen and how a browser should behave when you actually sleep.
Is the complete Script runtime sleeping? Normally it should because you only have one thread running your code. So what happens if other events oocur during sleep? they would block, and as soon execution continues all blocked events would fire. That will cause an odd behaviour as you might imagine (for instance mouse click events which are fired some time, maybe seconds, after the actual click). Or these events had to be ignored, which will lead to a loss of information.
What will happen to your browser? Shall it wait for sleep if the user clicks a (e.g. close window) button? I think not, but this might actually call javascript code again (unload) which will not be able to be called since program execution is sleeping.
On a second thought sleep is a sign of poor program design. Actually a program/function/you name it has a certain task, which shall be completed as soon as possible. Sometimes you have to wait for a result (for instance you wait for an XHR to complete) and you want to continue program execution meanwhile. In this case you can and should use asynchronous calls. This results in two advantages:
The speed of all scripts is enhanced (no blocking of other scripts due to sleep)
The code is executed exactly when it should and not before or after a certain event (which might lead to other problems like deadlocks if two functions check for the same condition ...)
... which leads to another problem: Imagine two or more pieces of code would call sleep. They would hinder themselves if they try to sleep at the same, maybe unnecessarily. This would cause a lot of trouble when you like to debug, maybe you even have difficulties in ensuring which function sleeps first, because you might control this behavior somehow.
Well I think that it is one of the good parts of Javascript, that sleep does not exist. However it might be interesting how multithreaded javascripts could perform in a browser ;)
javascript is desgined for single process single thread runtime, and browser also puts UI rendering in this thread, so if you sleep the thread, UI rendering such as gif animation and element's event will also be blocked, the browser will be in "not responding" state.
Maybe a combination of setTimeout and yield would work for your needs?
What's the yield keyword in JavaScript?
You could keep local function scope while letting the browser keep going about its work.
Of course that is only in Mozilla at the moment?
Because "sleep()" in JavaScript would make for a potentially horrible user experience, by freezing the web browser and make it unresponsive.
What you want is a combination of yield and Deferreds (from jquery for example).
It's called sometimes pseudoThreads, Light Threading or Green Threads. And you can do exactly what you want with them in javascript > 1.7 . And here is how:
You'll need first to include this code:
$$ = function (generator) {
var d = $.Deferred();
var iter;
var recall = function() {
try {var def = iter.send.apply(iter, arguments);} catch(e) {
if (e instanceof StopIteration) {d.resolve(); return;}
if (e instanceof ReturnValueException) {
d.resolve(e.retval); return
};
throw e;
};
$.when(def).then(recall); // close the loop !
};
return function(arguments) {
iter = generator.apply(generator, arguments);
var def = iter.next(); // init iterator
$.when(def).then(recall); // loop in all yields
return d.promise(); // return a deferred
}
}
ReturnValueException = function (r) {this.retval = r; return this; };
Return = function (retval) {throw new ReturnValueException(retval);};
And of course call jquery code to get the $ JQuery acces (for Deferreds).
Then you'll be able to define once for all a Sleep function:
function Sleep(time) {
var def = $.Deferred();
setTimeout(function() {def.resolve();}, time);
return def.promise();
}
And use it (along with other function that could take sometime):
// Sample function that take 3 seconds to execute
fakeAjaxCall = $$(function () {
yield (Sleep(3000));
Return("AJAX OK");
});
And there's a fully featured demo function:
function log(msg) {$('<div>'+msg+'</div>').appendTo($("#log")); }
demoFunction = $$(function (arg1, arg2) {
var args = [].splice.call(arguments,0);
log("Launched, arguments: " + args.join(", "));
log("before sleep for 3secs...");
yield (Sleep(3000));
log("after sleep for 3secs.");
log("before call of fake AjaxCall...");
ajaxAnswer = yield (fakeAjaxCall());
log("after call of fake AjaxCall, answer:" + ajaxAnswer);
// You cannot use return, You'll have to use this special return
// function to return a value
log("returning 'OK'.");
Return("OK");
log("should not see this.");
});
As you can see, syntax is a little bit different:
Remember:
any function that should have these features should be wrapped in $$(myFunc)
$$ will catch any yielded value from your function and resume it only when
the yielded value has finished to be calculted. If it's not a defered, it'll work
also.
Use 'Return' to return a value.
This will work only with Javascript 1.7 (which is supported in newer firefox version)
It sounds like what you're looking for here is a way to write asynchronous code in a way that looks synchronous. Well, by using Promises and asynchronous functions in the new ECMAscript 7 standard (an upcoming version of JavaScript), you actually can do that:
// First we define our "sleep" function...
function sleep(milliseconds) {
// Immediately return a promise that resolves after the
// specified number of milliseconds.
return new Promise(function(resolve, _) {
setTimeout(resolve, milliseconds);
});
}
// Now, we can use sleep inside functions declared as asynchronous
// in a way that looks like a synchronous sleep.
async function helloAfter(seconds) {
console.log("Sleeping " + seconds + " seconds.");
await sleep(seconds * 1000); // Note the use of await
console.log("Hello, world!");
}
helloAfter(1);
console.log("Script finished executing.");
Output:
Sleeping 1 seconds.
Script finished executing.
Hello, world!
(Try in Babel)
As you may have noticed from the output, this doesn't work quite the same way that sleep does in most languages. Rather than block execution until the sleep time expires, our sleep function immediately returns a Promise object which resolves after the specified number of seconds.
Our helloAfter function is also declared as async, which causes it to behave similarly. Rather than block until its body finishes executing, helloAfter returns a Promise immediately when it is called. This is why "Script finished executing." gets printed before "Hello, world!".
Declaring helloAfter as async also allows the use of the await syntax inside of it. This is where things get interesting. await sleep(seconds * 1000); causes the helloAfter function to wait for the Promise returned by sleep to be resolved before continuing. This is effectively what you were looking for: a seemingly synchronous sleep within the context of the asynchronous helloAfter function. Once the sleep resolves, helloAfter continues executing, printing "Hello, world!" and then resolving its own Promise.
For more information on async/await, check out the draft of the async functions standard for ES7.