I'm having problems canceling my XHR requests when navigating between pages. I have a page that has 8 requests that get fired off. I cancel them on click of a link outside of the current page. The page stalls as it waits on the next document to load. They XHR requests appear as cancelled in developer tools, but the new document stalls as if it is waiting for them to come back.
Here you can see the page is stalled even though all the other requests are cancelled. The new page is the only pending request...
And here you can see once the page finally did make the jump the TTFB is 52.52s. If I wait for the calls to come back before clicking away the jump is instant.
Here are the headers for the new pages once it finally loads if that helps...
I use the following frankenstein code to manage XHR requests. I have a cancelAll function towards the bottom that aborts the requests...
XHRManager = {
Requests: [],
pendingRequests: [],
addNextRequest: function (r) {
var timeout = 0;
if (trace.isDevelopment()) {
timeout = 350;
}
setTimeout(function () {
if (r.url == XHRManager.pendingRequests[0].url && r.start == XHRManager.pendingRequests[0].start) {
XHRManager.pendingRequests.splice(0, 1);
}
else {
$( XHRManager.pendingRequests).each(function (ii, dd) {
if (dd.url == r.url && dd.start == r.start) {
XHRManager.pendingRequests.splice(ii, 1);
}
});
}
XHRManager.startNextRequest();
if (trace.findLocalStorage()) {
XHRManager.showTrace = true;
trace.show();
}
}, timeout);
},
requests: [],
intervals: [],
requestsInt: 0,
firstRun: true,
delay: 500,
globalTimeout: 5000,
showTrace: false,
startNextRequest: function () {
$( XHRManager.pendingRequests).each(function (i, d) {
if (d.start) {
}
if (i == 0) {
if (trace.domWatcher.constructor == Function) {
trace.domWatcher(d.requestNumber);
}
trace.log("Request #" + d.requestNumber + " started");
d.requestType(d);
}
});
if ( XHRManager.pendingRequests.length == 0) {
if (trace.isDevelopment()) {
trace.show();
}
}
},
AddToPendingRequests: function (url, params, cb, type, errCB) {
var rI = XHRManager.requestsInt;
XHRManager.requestsInt++;
var req = {url: url, params: params, cb: cb, requestNumber: rI, requestType: type};
if (errCB) {
req.errCB = errCB;
}
XHRManager.pendingRequests.push(req);
// if(trace.findLocalStorage()){
// trace.show();
// }
if (rI == 0 || XHRManager.pendingRequests.length == 1) {
XHRManager.startNextRequest();
}
},
writeVals: function (url, params, data, start, cb, requestNumber) {
if ($("meta[content='development']").length > 0) {
try {
var response = {};
response.requestNumber = requestNumber;
if (data.sql != "" && data.sql != undefined) {
response.sql = data.sql;
}
if (data.debug) {
if (data.debug.sql != "" && data.debug.sql != undefined) {
response.sql = data.debug.sql;
}
}
if (data.data != "" && data.data != undefined) {
response.data = data.data;
}
else {
if (data != "" || data != undefined) {
response.data = data;
}
}
if (url != "" && url != undefined) {
response.url = url;
}
if (params != "" && params != undefined) {
response.params = params;
}
if (cb) {
response.cb = cb.toString();
}
else {
response.cb = "";
}
response.requestStats = {};
response.requestStats.start = start;
response.requestStats.end = Date();
response.requestStats.totalTime = ((new Date(response.requestStats.end)).getTime() - (new Date(start)).getTime()) / 1000 + " sec(s)";
XHRManager.Requests.push(response);
}
catch (e) {
trace.log(e);
}
}
},
_create: function (r) {
var xm = XHRManager;
var start = Date();
var req = $.get(r.url, r.params, r.cb)
.done(function (data) {
XHRManager.writeVals(r.url, r.params, data, start, r.cb, r.requestNumber);
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.addNextRequest(r);
}
});
xm.requests.push(req);
},
_createAjax: function (r) {
var xm = XHRManager;
var start = Date();
if (r.type == "PUT" || r.type == "DELETE") {
var req = $.ajax({
type: r.type,
xhrFields: {
withCredentials: true
},
url: r.url,
data: r.params,
success: function (data) {
XHRManager.writeVals(r.url, r.params, r.data, r.start, r.cb, r.requestNumber);
r.cb(data);
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.addNextRequest(r);
}
},
error: r.errCB
});
xm.requests.push(req);
}
else {
var req = $.ajax({
type: r.type,
xhrFields: {
withCredentials: true
},
dataType: 'json',
json: 'json',
url: r.url,
data: r.params,
success: function (data) {
XHRManager.writeVals(r.url, r.params, data, start, r.cb, r.requestNumber);
r.cb(data);
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.addNextRequest(r);
}
},
error: r.errCB
});
xm.requests.push(req);
}
},
_createJSON: function (r) {
var start = Date();
var xm = XHRManager;
var req = $.getJSON(r.url, r.params, r.cb)
.done(function (data) {
XHRManager.writeVals(r.url, r.params, data, start, r.cb, r.requestNumber);
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.addNextRequest(r);
}
});
xm.requests.push(req);
},
create: function (url, params, cb) {
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.AddToPendingRequests(url, params, cb, XHRManager._create);
}
else {
var r = {};
r.url = url;
r.params = params;
r.cb = cb;
XHRManager._create(r);
}
},
createAjax: function (url, params, type, cb, errCB) {
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.AddToPendingRequests(url, params, cb, XHRManager._createAjax, errCB);
}
else {
var r = {};
r.url = url;
r.params = params;
r.cb = cb;
r.type = type;
r.errCB = errCB;
XHRManager._createAjax(r);
}
},
createJSON: function (url, params, cb) {
if (trace.isDevelopment() && trace.isOn()) {
XHRManager.AddToPendingRequests(url, params, cb, XHRManager._createJSON);
}
else {
var r = {};
r.url = url;
r.params = params;
r.cb = cb;
XHRManager._createJSON(r);
}
},
remove: function (xhr) {
var xm = XHRManager;
var index = xm.requests.indexOf(xhr);
if (index > -1) {
xm.requests.splice(index, 1);
}
index = xm.intervals.indexOf(xhr.interval);
if (index > -1) {
xm.intervals.splice(index, 1);
}
},
cancelAll: function () {
var xm = XHRManager;
$(xm.requests).each(function () {
var t = this;
t.abort();
});
$(xm.intervals).each(function () {
var t = this;
clearInterval(t);
});
xm.requests = [];
xm.intervals = [];
}
};
The site uses jQuery, PHP, Zend Framework 2, and SQL, Apache. What am I missing?
Probable causal chain
the server does not realise the XHR requests are cancelled, and so the corresponding PHP processes keep running
these PHP processes use sessions, and prevent concurrent access to this session until they terminate
Possible solutions
Adressing either of the above two points breaks the chain and may fix the problem:
(a) ignore_user_abort is FALSE by default, but you could be using a non-standard setting. Change this setting back to FALSE in you php.ini or call ignore_user_abort(false) in the scripts that handle these interruptible requests.
Drawback: the script just terminates. Any work in progress is dropped, possibly leaving the system in a dirty state.
(b) By default, PHP will not detect that the user has aborted the connection until an attempt is made to send information to the client. Do echo something periodically during the course of your long-running script.
Drawback: this dummy data might corrupt the normal output of your script. And here too, the script may leave the system in a dirty state.
A PHP sessions is stored as a file on the server. On session_start(), the script opens the session file in write mode, effectively acquiring an exclusive lock on it. Subsequent requests that use the same session are put on hold until the lock is released. This happens when the script terminates, unless you close the session explicitely. Call session_write_close() or session_abort() as early as possible.
Drawback: when closed, the session cannot be written anymore (unless you reopen the session, but this is somewhat inelegant a hack). Also the script does keep running, possibly wasting resources.
I definitely recommend the last option.
Are you storing your Ajax Request on a variable?. If not, that's what you need to do to completely cancel a request
var xhr = $.ajax({
type: "POST",
url: "anyScript.php",
data: "data1=0&data2=1",
success: function(msg){
//Success Function
}
});
//here you abort the request
xhr.abort()
I assume that you have done that, but check all log files (php and apache).
Also try this:
php.ini
upload_max_filesize = 256M
post_max_size = 256M
.htaccess
php_value upload_max_filesize 256M
php_value post_max_size 256M
Another thing that bugs me is this part.
$(xm.requests).each(function () {
var t = this;
t.abort();
});
$(xm.intervals).each(function () {
var t = this;
clearInterval(t);
});
Try passing arguments to the callback and abort through them. I have seen cases, where assigning this to a variable withing $.each loop actually points to a different object or the global window.
$(xm.requests).each(function (index, value) {
value.abort();
});
$(xm.intervals).each(function (index, value) {
clearInterval(value);
});
Related
My Ajax is taking too much time on loading am calling ajax from 1 to 3000
It hit on database and get if value exist in database from 1 to 3000 then it will return
Here's my code
function Getdata(e) {
e = e;
jQuery.ajax({
type: "GET",
async: true,
url: "getdata.php",
data: "id=" + e,
success: function(t) {
jQuery(".reult_get_wish-" + e).html(t.htmltext)
},
dataType: "json"
})
}
for (var e = 1; e <= 3000; e++) {
Getdata(e);
}
Here's my getdata.php file code
$id = $_GET['id'];
$sql = "SELECT * from wishing_report where user = '".$id."'";
$result = $mysqli->query($sql);
if ($e = $result->fetch_array(MYSQLI_ASSOC))
{
echo json_encode($e);
}
Explained
If it takes some time, why not use an asynchronous approach, where you can process 'x' amount at a time, i.e. you could use setTimeout and recursion or setInterval, just so you can process a block of information/data at a time.
In this example you can see that there's an onIterate function and a onComplete function, both of these are used in different scenarios, you can use the onIterate function for each iteration, prior to the iterate function being complete. Once you've iterated enough, this is when you can fire the onComplete function, feel free to make any changes you like, i.e. include promises or whatever takes your fancy.
This could also be a better approach for the server as you're allowing the server time to recover from the last request. Alternatively you could alter your back end code so that it's more efficient, etc, you could use some limit and offset parameter(s) within your query to ensure the server isn't handling too much data at one time.
// A function to fire when the ajax request has finished.
const onSuccess = data => {
console.log(data);
};
// Simulate the ajax request.
const getData = (i, callback) => {
setTimeout(() => {
console.log(i);
return callback(i);
}, 500);
}
// A function to fire once complete.
const onComplete = () => console.log('Finished');
// A function to fire if it's not finished/complete.
const onIterate = () => console.log('NOT finished yet');
// A function to iterate, break the loop up into chuncks.
const iterate = (start, end, delay) => {
const process = data => {
iterate(++start, end, delay)
if (start > end) {
onComplete(data);
} else {
onIterate(data);
}
};
if (start <= end) {
setTimeout(() => {
getData(start, process);
}, delay);
}
};
// A starting point.
const start = () => iterate(0, 10, 1500);
// Just start the function.
start();
Your Ajax is taking a lot of time beacause you're running it 3000 times. To avoid calling it many times, I recommend putting all ids in array. I would do something like this.
JavaScript:
function Getdata(e) {
e = e;
jQuery.ajax({
type: "POST",
async: true,
url: "getdata.php",
data: {id: e},
success: function(t) {
$.each(t, function() {
$.each(this, function(k, v) {
jQuery(".reult_get_wish-" + v).html(v.htmltext);
});
});
},
dataType: "json"
})
}
var arr = [];
for (var e = 1; e <= 3000; e++) {
arr.push(e);
}
Getdata(arr);
PHP:
$id = $_POST['id'];
$sql = "SELECT * from wishing_report where user IN ('".implode(',', $id)."')";
$result = $mysqli->query($sql);
if ($e = $result->fetch_array(MYSQLI_ASSOC))
{
echo json_encode($e);
}
I am making a cart system using PHP and AJAX. Everything works pretty fine, except for the updating part. When the user clicks outside of the number form, the subtotal will update automatically. I used AJAX for this, but doesn't work. I tested this with the search, everything was fine.
AJAX function:
function initXML() { //Adaptation for old browsers
var _xmlhttp;
if (window.XMLHttpRequest) {
_xmlhttp = new XMLHttpRequest();
} else {
_xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
return _xmlhttp;
}
function ajaxFormValidate(_config) {
/*Structure
type: 'GET' or 'POST',
url: 'request URL' Default: location.href,
method: true or false (optional). False for non-async, true for async,
sendItem: file or data to be sent,
success: a callback function when the request is complete
error: a fallback function when the request is failed
*/
if (!_config.type) {
_config.type = 'POST'; //Automatically set type to POST if no type property is declared
}
if (!_config.url) {
_config.url = location.href; //Automatically set url to self if no url property is declared
}
if (!_config.method) {
_config.method = true; //Automatically set method to true if no method property is declared
}
var _xmlHttp = initXML(); //Declare request variable
_xmlHttp.onreadystatechange = function(){
if (_xmlHttp.readyState === 4 && _xmlHttp.status === 200) {
if (_config.success) {
_config.success(_xmlHttp.responseText);
}
}
else {
if (_config.error) {
_config.error(_xmlHttp.responseText);
}
}
}; //Check readyState and status to handle the request properly
//Handle the items sent
var _Itemstring = [], _sendItem = _config.sendItem;
if (typeof _sendItem === "string") {
var _arrTmp = String.prototype.split.call(_sendItem, '&');
for (var i = 0; i < _arrTmp.length; i ++) {
var _tmpData = _arrTmp[i].split('=');
_Itemstring.push(encodeURIComponent(_tmpData[0]) + "=" + encodeURIComponent(_tmpData[1]));
}
}
else if (typeof _sendItem === "object" && !(_sendItem instanceof String || (FormData && _sendItem instanceof FormData))) {
for (var k in _sendItem) {
var _tmpData = _sendItem[k];
if (Object.prototype.toString.call(_tmpData) === "[object Array]") {
for (var j = 0; j < _tmpData.length; j ++) {
_Itemstring.push(encodeURIComponent(k) + '[]=' + encodeURIComponent(_tmpData[j]));
}
}
else {
_Itemstring.push(encodeURIComponent(k) + '=' + encodeURIComponent(_tmpData));
}
}
}
_Itemstring = _Itemstring.join('&');
if (_config.type === 'GET') {
_xmlHttp.open('GET', _config.url + "?" + _Itemstring, _config.method);
_xmlHttp.send();
}
else if (_config.type === 'POST') {
_xmlHttp.open('POST', _config.url, _config.method);
_xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
_xmlHttp.send(_Itemstring);
}
}
AJAX called inside a JS file handling the changes in the inputs
// JavaScript Document
window.addEventListener('load', function(){
var _ranum = document.getElementsByClassName('ranum');
for (var i = 0; i < _ranum.length; i ++) {
_ranum[i].addEventListener('blur', function(){ //Check click outside
var _this = this;
ajaxFormValidate({
type:'POST',
sendItem: {
u: _this.value, //Send the value after the change
id: _this.id, //Send the product id
},
success: function(response){
console.log('SUCCESS');
}
});
}, false);
}
}, false);
Handling the changes in PHP file
var_dump($_POST['u']);
if (isset($_POST['id'], $_POST['u'])) {
if (!empty($_POST['id']) && !empty($_POST['u'])) {
$id = mysqli_real_escape_string($conn, $_POST['id']);
$u = mysqli_real_escape_string($conn, $_POST['u']);
if (isset($_SESSION['cart'][$id])) {
$_SESSION['cart'][$id] = $u;
}
}
}
I see the it logged out 'SUCCESS' in the console, however, when I use var_dump($_POST['u']) it doesn't work. Also, it updates the subtotal only if I reload the page.
What did I do wrong? I pretty sure my AJAX function is correct, and JS logged out 'SUCCESS', so what's the problem? Thanks very much
Good day,
I am trying to create a script that loads my Browser Geolocation and following sends it to a file that saves it.
The problem is. The data does not get send.
And an even bigger problem is that I have tried many things but I am quite clueless.
I added several alerts but the alerts do not show up.
What should the script do?
Run once every five seconds and requesting your GeoLocation.
When you click accept on your phone and accept for all from this source you will have an active GPS alike tracking.
The code :
<script type="text/javascript">
function success(position) {
///SaveActiveGeoLocation();
}
function error(msg) {
var s = document.querySelector('#status');
s.innerHTML = typeof msg == 'string' ? msg : "failed";
s.className = 'fail';
// console.log(arguments);
}
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(success, error);
}
else{
error('not supported');
}
function SaveGeoLocation(){
var Lat = position.coords.latitude;
var Lon = position.coords.longitude;
var Accuracy = position.coords.accuracy;
///######## SENDING THE INFORMATION BY AJAX
$.ajax({
type : "POST", /// **** SEND TYPE
url : "savegeo.php", /// **** TARGET FILE TO FETCH THE DATA
data : {
'Lat' : Lat,
'Lon' : Lon,
'GeoAccuracy' : Accuracy
},
///######## IN CASE OF SUCCESS
success:function(response){
if( response == "ok" ){
alert('SEND!');
}
else{
alert( "Response = " + response );
}
}
}
);
}
$(document).ready(function() {
$.ajaxSetup({
cache: false
}); // This part addresses an IE bug. without it, IE will only load the first number and will never refresh
setInterval(function() {
///alert('HOI!');
SaveGeoLocation();
}, 5000);
// the "10000" here refers to the time to refresh the div. it is in milliseconds.
/// **** DEFAULT LOADING
///SaveGeoLocation();
});
</script>
The file that saves the send POST data :
<?php
include('function.geolocation.class.php');
$geo = new GeoLocation();
$Lat = $_POST['Lat'];
$Lon = $_POST['Lon'];
$GeoAccuracy = $_POST['GeoAccuracy'];
$IP = $geo->GetIP();
$file = 'location.txt';
$address = $geo->getAddress($Lat, $Lon);
$contents = $Lat.'|'.$Lon.'|'.$IP.'|'.$GeoAccuracy.'|'.date('Y-m-d H:i:s').'|'.$address.PHP_EOL;
$handle = fopen($file, 'a');
fwrite($handle, $contents);
fclose($handle);
echo 'ok';
?>
One problem I can see is the variable position does not exists in the context of the SaveGeoLocation method
function success(position) {
//SaveActiveGeoLocation();
window.position = position;
}
function SaveGeoLocation() {
if (!window.position) {
return;
}
//your stuff
}
There is no need to call SaveGeoLocation using interval, you can call SaveGeoLocation from the success callback like
function success(position) {
SaveActiveGeoLocation(position);
}
function SaveGeoLocation(position) {
//your stuff
}
If you want to save the location continuously
$(document).ready(function () {
$.ajaxSetup({
cache: false
});
function saveLocation() {
navigator.geolocation.getCurrentPosition(success, error);
}
function success(position) {
var Lat = position.coords.latitude;
var Lon = position.coords.longitude;
var Accuracy = position.coords.accuracy;
///######## SENDING THE INFORMATION BY AJAX
$.ajax({
type: "POST", /// **** SEND TYPE
url: "savegeo.php", /// **** TARGET FILE TO FETCH THE DATA
data: {
'Lat': Lat,
'Lon': Lon,
'GeoAccuracy': Accuracy
},
///######## IN CASE OF SUCCESS
success: function (response) {}
}).done(function (response) {
if (response == "ok") {
alert('SEND!');
} else {
alert("Response = " + response);
}
}).always(function () {
setTimeout(saveLocation, 5000)
});
}
function error(msg) {
var s = document.querySelector('#status');
s.innerHTML = typeof msg == 'string' ? msg : "failed";
s.className = 'fail';
}
if (navigator.geolocation) {
saveLocation();
} else {
error('not supported');
}
});
Hi all the following function will work and do exactly as I want it to but I want this to be a .post not a .get can anyone see a problem with the following? its pretty much straight from another answer on stack overflow and should work fine.
jQuery(document).ready(function() {
//This function adds a development.
jQuery('#add_dev').bind('submit', function(e) {
e.preventDefault();
var data = {
action: 'AjaxAddDev',
security: AjaxHandler.ajaxnonce,
name: jQuery('#dev_name').val(),
desc: jQuery('#dev_desc').val()
};
//alert(data['name']+data['desc']);
jQuery.get(
AjaxHandler.ajaxurl,
data,
function(response) {
// ERROR HANDLING
if (!response.success) {
// No data came back, maybe a security error
if (!response.data) {
//$('#my-answer').html('AJAX ERROR: no response');
alert("Problem adding Development");
} else {
//$('#my-answer').html(response.data.error);
alert(response.data);
}
} else {
//$('#my-answer').html(response.data);
alert("Problem adding Development");
}
}
);
});
});
The error I get when I set it to .post is:
l.send(n.hasContent && n.data || null), r = function (e, i) {
Which is line 2963 of an un-minified version of jquery
/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license */
Can anyone point me in the right Direction?
Updated Code:
jQuery(document).ready(function() {
//This function adds a development.
jQuery('#add_dev').bind('submit', function(e) {
e.preventDefault();
var data = {
action: 'AjaxAddDev',
security: AjaxHandler.ajaxnonce,
name: jQuery('#dev_name').val(),
desc: jQuery('#dev_desc').val()
};
//alert(data['name']+data['desc']);
jQuery.ajax({
url: AjaxHandler.ajaxurl,
type: "POST",
data: data,
success:function(data) {
// This outputs the result of the ajax request
alert(data);
},
error: function(errorThrown){
alert(errorThrown['error']);
}
});
});
});
I am using firefox latest version,
I got the following returned as an errotThrowen['error']
function () {
if (l) {
var t = l.length;
(function i(t) {
x.each(t, function (t, n) {
var r = x.type(n);
"function" === r ? e.unique && p.has(n) || l.push(n) : n && n.length && "string" !== r && i(n)
})
})(arguments), n ? o = l.length : r && (s = t, c(r))
}
return this
}
if you want to ajax on change
$("#yourid").change(function () {
var p = {
postfieldname: value,
postfieldname: value,
postfieldname: value,
postfieldname: value,
postfieldname: value,
postfieldname: value,
postfieldname: value,
}
$.ajax({
url: "library/test.php",
type: "POST",
data: p,
success: function (e) {
var t = jQuery.parseJSON(e);
$("#id").val(t['a']);
}
})
})
and on test.php
$array = array("a" => "test", "b" => "array");
$encode = json_encode($aray);
echo $encode;
OK this was kind of an odd one,
To get it working I simply had to add the following as the post URL.
url: AjaxHandler.ajaxurl+"&security="+AjaxHandler.ajaxnonce,
If I left the security out of the url it would fail, I don't know why but this had me going around in circles for hours.
The symptoms: I have the "push_listener_concurrency" set to "broadcast" but only several (random amount) of subscribers get the message. Was anybody dealing with this type of problem? The test cases are pretty straight-forward I 'POST' to a '/publisher?cid=test' channel and 'GET' from a '/listener?cid=test' everything seems to be working when there's only one client per channel...
Just in case somebody will face the same problem.
There were a problem with headers. To listen the nginx http push module correctly with jQuery you should use a listener that processes the headers correctly here is my code:
function Listener(url, successCallback, failureCallback) {
var scope = this;
var etag = 0, lastModified = 0;
var launched = false;
var failure = false;
this.successTimeout = 0;
this.failureTimeout = 5000;
var getTimeout = function () {
return failure ? this.failureTimeout : this.successTimeout;
};
var listen = function () {
$.ajax(scope.ajaxOptions);
}
var beforeSend = function (jqXHR) {
jqXHR.setRequestHeader("If-None-Match", etag);
jqXHR.setRequestHeader("If-Modified-Since", lastModified);
};
var complete = function (jqXHR) {
var timeout = getTimeout();
etag = jqXHR.getResponseHeader('Etag');
lastModified = jqXHR.getResponseHeader('Last-Modified');
var timeout = jqXHR.statusText == 'success' ? scope.successTimeout : scope.failureTimeout;
if (timeout > 0) {
setTimeout(listen, timeout);
} else {
listen();
}
};
this.ajaxOptions = {
url : url,
type : 'GET',
async : true,
error : failureCallback,
success : successCallback,
dataType : 'json',
complete : complete,
beforeSend : beforeSend,
timeout: 1000 * 60 * 60 * 24
};
this.start = function (timeout) {
if (!launched) {
if (typeof(timeout) == 'undefined' || timeout == 0) {
listen();
} else {
setTimeout(listen, timeout);
}
launched = true;
}
};
}