Κι άκου τώρα τί παθαίνει ο άνθρωπος αν μπλέξει με ... μπελάτες ...
Υπάρχει μια σελιδούλα που λέτε, η οποία κάνει περιοδικά AJAX refresh calls στον εαυτό της, κάνοντας monitor το status μιας δουλειάς που τρέχει στο server.
Αυτό το job στο server λοιπόν, που και πού δίνει πίσω warnings (
κατά τη διάρκεια της λειτουργίας του, όχι μόνο στο τέλος της ), κι όταν με το καλό τελειώσει, πρέπει τότε να εκτελεστεί κάτι άλλο, κι αυτό όμως ξεκινάει στο UI.
Τα warnings λοιπόν, φαίνονταν στην αρχή με alert boxes. Μετά όμως, επειδή ο μπελάτης δε γούσταρε γκρί pop-ups που του χαλάγανε την αισθητική (
κι αν δεν πατήσεις και οκ μπλοκάρουν το browser ο οποίος περιμένει να πατήσεις το οκ ... οπότε πάνε τα ajax refreshes ... ), πετάγονταν σε ένα ψευτο-pop-up με ένα div-άκι όμορφούλι.
Μια χαρά ως εδώ ... αλλα το div-άκι ήταν .. singleton ! Οπότε, μόλις του λες "
δείξε αυτό", δείχνει ... αυτό, κι αν στα καπάκια του πείς "
δείξε κάτι άλλο", τότε δείχνει κάτι άλλο.
"
Α ... δεν το θέλω αυτό ... " λέει ο κακός μπελάτης ... "
γιατί εξαφανίζεται προτού διαβάσω το μήνυμα λάθους και πατήσω το κίτρινο 'οκ' ... " ... και κλάμα εμείς, γιατί το ένα χαλάει τα refresh, το άλλο χαλάει ... τον πελάτη

Καίγοντας λίγο το μυαλό μου, σκέφτηκα το εξής. Αυτο που ήθελα στην πραγματικότητα, ήταν ένας τρόπος - σε Javascript - να μην εκτελείται μια κλήση μου, αν πρώτα δεν έχει γίνει κάτι άλλο. Κι αυτό, χωρίς να μπλοκάρω το browser, ώστε να τρέχουν και τυχόν άλλα scripts παράλληλα. (
¨ενα while loop απο την άλλη, μπορεί επίσης να στείλει τη CPU σας στο ... 500% πολύ γρήγορα στον I.E. )
. . .
Άντε, λέω ... ένα while ( !myCondition ) setTimeout( callMeAgain ) είναι, τίποτα σπουδαίο.
Και μετά τρώς τη συνειδητοποίηση. Το setTimeout παίρνει το script που είναι να τρέξει ως string. Εγώ όμως έχω παραμέτρους για την κλήση μου, οι οποίες δεν είναι σε global scope, δε μπορώ να κάνω eval ένα script με τα ονόματά τους μέσα !!! Κλάμμαααααα .....

Οπότε; Τι κάνεις τώρα;
Μετά απο λίγη σκέψη ακόμα λοιπόν, κατέληξα στο εξής όμορφο ...
// I'm planning to store the "when" and the "callthis" functions here, under a unique key.
var _executeWhenMap = { };
/*
Executes funcToExecute only after funcWhen returns true, otherwise
it will wait for a few millis, and try again.
*/
function ExecuteWhen(funcToExecute, funcWhen) {
/* ok, what I want to do is this:
If my when() function returns FALSE, I'm building a little struct with the
when() and toExecute() functions, ans store it in the global executeWhenMap[] under a unique key.
Then, I just setTimeout(...) executeWhenInternal, with the key as the argument ;)
*/
if(funcWhen())
funcToExecute();
else {
// create my little ExecuteInfo here ...
var executeInfo = { "When":funcWhen, "Do":funcToExecute };
var myUniqueKey = (new Date()).getMilliseconds().toString();
_executeWhenMap[myUniqueKey] = executeInfo;
setTimeout("ExecuteWhenInternal('" + myUniqueKey + "');", 10);
}
}
/*
Queries the executionMapStack thingy for a given executionInfo key,
and does the .. if(when()) do() else setTimeout(executeWhenInternal(myOriginalKey), 10 ) thing ... and God be with us ...
*/
function ExecuteWhenInternal(strExecuteInfoKey){
// ok, na doume an exw eceuteInfo gia ayto to key edw ...
var executeInfo = _executeWhenMap[strExecuteInfoKey];
// exw ???
if(null == executeInfo || "undefined" == typeof(executeInfo))
throw "ExecutionInfo for Key: " + strExecuteInfoKey + " was not found in the execution map";
else {
var funcWhen = executeInfo.When;
var funcToCall = executeInfo.Do;
if(funcWhen()){
funcToCall();
_executeWhenMap[strExecuteInfoKey] = null;
} else
setTimeout("ExecuteWhenInternal('" + strExecuteInfoKey + "');", 10);
}
}
Τι κάνει τώρα τούτο δώ ... στην πραγματικότητα, κάνει αυτό που δεν κάνει η JavaScript απο μόνη της. Κάνει τη μέθοδο που θέλα να εκτελέσω, μαζί με τη μέθοδο η οποία ελέγχει αν μπορώ να την εκτελέσω ... global, και string-addressable.
Υπάρχει δηλαδή ένα Dictionary στη μνήμη, το οποίο δέχεται entries βάσει ενός string key, τα οποία περιέχουν τις 2 μεθόδους, τη "when" μέθοδο ( μπορώ να εκτελέσω τον κώδικά μου τώρα; ), και τη μέθοδο που τελικά θέλω να εκτελεστεί ( αυτό είναι ο κώδικας που ήθελα να εκτελέσω απ'την αρχή ... ).
Έτσι, μπορώ να κάνω setTimeout, και μέσα εκεί να βλέπω αν πρέπει να εκτελέσω ακόμα ή να περιμένω, και να πράξω πλέον τα απαραίτητα execute ή setTimeout στον εαυτό μου για αργότερα.
Το δοκίμασα σε μια html σελίδα με 2 κουμπάκια. Το πρώτο κάνει ένα απλό alert('hello'). Το δεύτερο ανοίγει και κλείνει μια boolean μεταβλητή. Θέλω τα alert να εκτελεστούν, μόνο αν η boolean είναι "ανοιχτή". Αλλιώς, θα εκτελεστούν αν και όταν ανοίξει ποτέ αυτή η boolean !
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Untitled Page</title>
<script type="text/javascript">
var _continue = false;
function CanIContinue() {
return _continue;
}
window.status = !_continue;
</script>
<script type="text/javascript" src="ExecuteAfter.js" > </script>
</head>
<body>
<input type="button" value="Start/End Blocking" onclick="_continue = !_continue; window.status = !_continue;" />
<input type="button" value="Try alert('hello');" onclick="ExecuteWhen(function() { alert('Hello !!!');}, CanIContinue );" />
</body>
</html>
Και ως δια μαγείας ... έπαιξε !!! Ανοίγω το "blocking", πατάω 2 - 3 κλικ στο "alert" κουμπί .. και δε γίνεται τίποτα. Μόλις κλείσω το "blocking" ... μαγικά εκτελούνται ΟΛΑ τα κλικ που είχα κάνει κλίκ πριν

Τώρα αν σκέφτεστε γιατί σας πρήζω με όλα αυτά τα JavaScript-ικά (
η ρήμα με τα Σανσκριτικά δεν είναι τυχαία ... ), η απάντηση είναι απλή. Είναι πολύ όμορφο, και χρήσιμο. Αν το τραβήξει κανείς, με αυτή τη μεθοδούλα που επιστρέφει το boolean (
μπορώ να συνεχίσω την εκτέλεση; ), μπορείς να δέσεις και να κάνεις synchronization μεθόδων, ουρές, multiple event handlers και chaining κτλ. κτλ. είναι ... execution chain & workflow παρεούλα, σε JavaScript παρακαλώ. Θέλει μόνο φαντασία :)
Ελπίζω να μη βαρεθήκατε όσοι φτάσατε μέχρι το τέλος, καλό απόγευμά μας !
O:]
Angel
O:]