I'm checking the server if a job is done. I don't want to spam the server so that's why I will use setInterval.
I call the trigger function, and when the job is done on the server (perhaps 2-3 calls before it's done), the function is done.
I know that I could call my finishFunction in the interval and kind of solve it. But I would like to return it because I call the trigger function from another js file. And if it's possible, I would like to handle it there.
function trigger() {
var response = startInterval();
response.then(function() {
//done finishFunction()
});
}
function checkServer() {
var obj = { test: true }
var respons = $.ajax({
url: "MyUrl",
data: JSON.stringify({ obj: obj }),
type: "POST",
contentType: "application/json",
dataType: "JSON",
cache: false
});
return respons;
}
function startInterval() {
var inProgress = false;
var interval = setInterval(function () {
if (!inProgress) {
inProgress = true;
var response = checkServer().then(function (data) {
var isDoneInQueue = JSON.parse(data.d);
if (isDoneInQueue) {
//finishFunction()???
clearInterval(interval);
return response;
};
inProgress = false;
});
}
}, 1000);
}
Return a Deferred object from the function, and resolve it when the server job is done:
function startInterval() {
var result = $.Deferred();
var inProgress = false;
var interval = setInterval(function () {
if (!inProgress) {
inProgress = true;
checkServer().then(function (data) {
var isDoneInQueue = JSON.parse(data.d);
if (isDoneInQueue) {
clearInterval(interval);
result.resolve(data);
};
inProgress = false;
});
}
}, 1000);
return result;
}
Whatever you call the resolve method with, is sent to the function that you use with the then method:
function trigger() {
startInterval().then(function(data) {
//done finishFunction()
});
}
Related
In my application, I'm trying to use sendBeacon to send data to my remote server. One of the data that I need is how many clicks its been on the page and I'm doing it as follows:
var clickNumber = 0;
document.addEventListener("mouseup", function () {clickNumber++;});
var SendToRemote = window.SendToRemote || [];
SendToRemote.init({
clicks: clickNumber
});
My sendBeacon
navigator.sendBeacon = (url, data) =>
window.fetch(url, { method: 'POST', body: {data: data}, credentials: 'include' });
My only issue now is that the clickNumber is always 0 (which is the default value) and even that mouseup does increment clickNumber, but when sending it sends 0.
How am I able to update the clickNumber so when sendBeacon is triggered, it gets the incremented/updated clickNumber instead of 0.
This is my SendToRemote.init which works fine: (PS: I have removed parts of the codes as it would be over 1000 lines, but kept whats needed):
if (!SendToRemote) {
var SendToRemote = (function(){
var defaults = {
endpoints: {
unload: "https://remote.com"
},
processData: function(results){},
},
results = {
click: 0,
// more stuff here
},
support = !!document.querySelector && !!document.addEventListener,
settings;
var actions = {
sendData: function () {
results.hmn = parseInt(actions.hmnDetection(
results.times.tt, results.times.tp, results.click, results.mouse, results.touch, results.scroll, results.tab
));
let url = settings.endpoints.unload,
data = JSON.stringify(results);
navigator.sendBeacon(url, data);
},
// more functions here
}
function init(options) {
document.addEventListener('DOMContentLoaded', (event) => {
// More stuff here
// Event Listener to porcess
if(modifiable.processOnAction){
let node = document.querySelector(modifiable.selector);
if(!!!node) throw new Error('Selector was not found.');
actions._e(node, modifiable.event, `init-${modifiable.selector}-processOnAction`, function() {
let nodeInput = document.getElementsByName(modifiable.element)[0];
if(!!!nodeInput) throw new Error('nodeInput was not found.');
nodeInput.value = JSON.stringify(results);
hasProcessed = true;
})
}
addEventListener('unload', (event) => {
if (!navigator.sendBeacon) {
navigator.sendBeacon = (url, data) =>
window.fetch(url, { method: 'POST', body: {data: data}, credentials: 'include' });
}
if (!hasProcessed) {
actions.sendData();
hasProcessed = true;
}
return;
});
});
}
function processResults(){
if(settings.hasOwnProperty('processData')){
if (!modifiable.processOnAction){
return settings.processData.call(undefined, results);
}
return results;
}
return false;
}
// Module pattern, only expose necessary methods
return {
init: init,
processResults: processResults,
};
})();
}
Thanks in advance!
I'm trying to rerun a failed AJAX call 3 times. After the third attempt, I'd like to call a failed method. I don't want the AJAX calls to over run each other though.
What's the safest/best way to achieve this with what I'm working with?
I'm using a globalAjaxRequest method like so:
globalAjaxRequest(request, successCallback, errorCallback) {
let ajaxRequest = null;
if (request.url) {
const ajaxOptions = {
type: request.method ? request.method.toUpperCase() : 'POST',
url: request.url,
data: request.data || undefined,
beforeSend: request.beforeSend,
success: (data) => {
successCallback(data);
},
error: (data) => {
if (errorCallback) {
errorCallback(data);
}
}
};
ajaxOptions.dataType = request.dataType || 'json';
ajaxOptions.contentType = request.contentType || 'application/json; charset=utf-8';
if (request.contentType) {
ajaxOptions.data = $.parseJSON(JSON.stringify(ajaxOptions.data));
} else {
ajaxOptions.data = JSON.stringify(ajaxOptions.data);
}
ajaxRequest = $.ajax(ajaxOptions);
}
return ajaxRequest;
}
}
Here's my attempt:
callAPI() {
const callData = {
url: '/callApi',
data: {
id: 'something'
}
};
global.Utils.globalAjaxRequest(callData, (success) => {
console.log('success');
successMethod();
}, (fail) => {
for (let i = 1;; i++) {
i <= 3 && setTimeout(() => {
callAPI();
}, 1000);
if (i > 3) {
failedMethod();
break;
}
}
});
}
callAPI();
You can't retry an asynchronous operation such as $.ajax() synchronously, so I'll assume that you just meant you want to automatically retry sequentially if it fails.
Here's a generic retry function for $.ajax():
// general purpose promise delay, useful when you want to delay a promise chain
function pDelay(t, v) {
return new Promise(function(resolve) {
setTimeout(resolve, t, v);
});
}
// three arguments:
// options: value for $.ajax(options) - does not support other forms of calling $.ajax()
// delay: amount of time in ms to delay before each retry (can be 0 if you want)
// retries: number of times to retry, defaults to 3 if you don't pass it
$.ajaxRetry = function(options, delay, retries) {
// default value for retries is 3 if the argument is not passed
let retriesRemaining = retriesRemaining !== undefined ? retriesRemaining: 3;
let opts = Object.assign({}, options);
function run() {
return $.ajax(opts).catch(function(err) {
--retriesRemaining;
// don't fire this more than once
delete opts.beforeSend;
if (retriesRemaining > 0) {
// try again after short delay
return pDelay(delay).then(run);
} else {
// hit max retries, propagate error back to caller
throw e;
}
});
}
return run();
}
FYI, this code assumes that "failure" in your case means that the promise that $.ajax() rejects. If "failure" means something else (such as looking at some result you got), then you will have to insert that additional test into the retry loop or expose a callback where that additional test can be provided externally.
To integrate this into your wrapper, you could do this:
globalAjaxRequest(request, successCallback, errorCallback) {
let ajaxRequest = null;
if (request.url) {
const ajaxOptions = {
type: request.method ? request.method.toUpperCase() : 'POST',
url: request.url,
data: request.data || undefined,
beforeSend: request.beforeSend,
};
ajaxOptions.dataType = request.dataType || 'json';
ajaxOptions.contentType = request.contentType || 'application/json; charset=utf-8';
if (request.contentType) {
ajaxOptions.data = $.parseJSON(JSON.stringify(ajaxOptions.data));
} else {
ajaxOptions.data = JSON.stringify(ajaxOptions.data);
}
errorCallback = errorCallback || function(err) { throw err; };
ajaxRequest = $.ajaxRetry(ajaxOptions, 0, 3).then(successCallback, errorCallback);
}
return ajaxRequest;
}
}
FYI, it is kind of odd to take a promise interface and turn it back into plain callbacks. It seems you should just get rid of successCallback and errorCallback let the caller use the returned promise.
I'd do something like this that uses a closure to keep a counter above the async request:
globalAjaxRequest(request, successCallback, errorCallback, maxRequests) {
maxRequests = maxRequests || 1;
var requests = 1;
function ajaxRequest(request){
if (request.url) {
const ajaxOptions = {
type: request.method ? request.method.toUpperCase() : 'POST',
url: request.url,
data: request.data || undefined,
beforeSend: request.beforeSend,
success: (data) => {
successCallback(data);
},
error: (data) => {
if (requests < maxRequests){
requests++;
ajaxRequest(request);
} else if (errorCallback) {
errorCallback(data);
}
}
};
ajaxOptions.dataType = request.dataType || 'json';
ajaxOptions.contentType = request.contentType || 'application/json; charset=utf-8';
if (request.contentType) {
ajaxOptions.data = $.parseJSON(JSON.stringify(ajaxOptions.data));
} else {
ajaxOptions.data = JSON.stringify(ajaxOptions.data);
}
return $.ajax(ajaxOptions)
}
ajaxRequest(request);
}
I get desired output from below code. These are two JavaScript function expressions for printing data
(function(){
var getData=function()
{
console.log("getdata");
},
setData=function()
{
getData();
console.log("setData");
};
setData();
})();
But when I try something like this in another page.I didn't get desired output.
This is my code.
var employeesList = {
getemployees: function () {
var data= getData("Employees/GetEmployees");
},
init: function () {
this.getemployees();
}
}.init();
var getData = function (url) {
var error = false;
$.ajax({
type: 'GET',
url: url,
dataType: 'json',
success: function (data) {
return data;
},
error: function () {
return error = true;
}
});
};
I got an error like this.
getData is not a function
please help.
You can't use a variable before have defined it, so you have to declare getData before employeesList
The variable data in getemployees couldn't be accessible, I added a return there, so you can use its value in init
var getData = function (url) {
var error = false;
/*$.ajax({
type: 'GET',
url: url,
dataType: 'json',
success: function (data) {
return data;
},
error: function () {
return error = true;
}
});*/
return 'test_result';
};
var employeesList = {
getemployees: function () {
//Here you should return the result
return getData("Employees/GetEmployees");
},
init: function () {
var res = this.getemployees();
console.log(res);
}
}.init();
I hope it was clear, bye.
There are two methods to define "function":
var func_name = function(){...}
function func_name() {...}
The difference is that when you use the second one, it can be called before being declared.
var employeesList = {
getemployees: function () {
var data= getData("Employees/GetEmployees");
},
init: function () {
this.getemployees();
}
}.init();
//var getData = function (url) {
function getData (url) { // <====== change to this
var error = false;
$.ajax({
type: 'GET',
url: url,
dataType: 'json',
success: function (data) {
return data;
},
error: function () {
return error = true;
}
});
};
I have a main function where I want to check if a record exists or not in order to create or update the record, so in this function I am calling a helper function that checks for that record using ajax call, and then I want a true/false to be returned to the main function, but do I return defrred.resolve() and deferred.reject(), and how do I check on them? I can't seem to be able to implement it in promises.
Here's my code below, any hint is appreciated.
function _mainFunction()(
var recordID = prompt("Enter Desired Record ID");
var promise = _helperFunction(recordID);
promise.then(...) //do some processing when the reocrd is created or updated
)
function _helperFunction(passedId){
if (passedId) {
if (!_isRecordExists(passedId)) {
// if record doesn't exist, create it.
}
}
}
function _isRecordExists(passedId){
var decision;
var baseUrl = "some url";
var dfd = $.ajax({
url: baseUrl,
type: "GET",
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose"
}
});
dfd.promise().then(
function(data, status, jqXHR){
decision = true;
dfd.resolve();
},
function (jqXHR, status, error) {
decision = false;
dfd.reject();
});
return decision; // do I return decision here for true or false?
}
}
You need to return promise object from _isRecordExists function. Then in _helperFunction if-block wouldbe transformed into success/error callbacks of the promise returned from the previous check:
function _mainFunction() {
var recordID = prompt("Enter Desired Record ID");
var promise = _helperFunction(recordID);
promise.then(function() {
console.log('Do something else');
}, function() {
console.log('Failed to find and create new. Maybe try again');
});
}
function _helperFunction(passedId) {
return $.Deferred(function(deferred) {
if (passedId) {
_isRecordExists(passedId).then(function(recordObj) {
// record exists, do something with it
console.log('Exists');
deferred.resolve(recordObj);
}, function() {
// record doesn't exist, create it.
console.log('Does not exist');
deferred.reject();
});
}
deferred.reject();
}).promise();
}
function _isRecordExists(passedId) {
var decision;
var baseUrl = "some url";
return $.ajax({
url: baseUrl,
type: "GET",
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose"
}
});
}
Here is also a rewritten _helperFunction implemented with a real promises (either with a polyfill or native):
function _helperFunction(passedId) {
if (passedId) {
return Promise.resolve(_isRecordExists(passedId)).then(function(recordObj) {
// record exists, do something with it and pass further
return recordObj;
}, function() {
// record doesn't exist, create it
return createNewRecord(); // createNewRecord should return new promise
});
}
return Promise.reject();
}
function _mainFunction(){
var recordID = prompt("Enter Desired Record ID");
_helperFunction(recordID)
.then(function(decision) {
// you can use decision here
})
.catch(function(decision) {
// passing decision as true or false in _isRecordExists fn, just for information
// or future usages
// then / catch status already gives the idea
});
}
function _helperFunction(passedId){
var deferred = $.deferred();
if (passedId) {
_isRecordExists(passedId))
.then(function(data) {
// then means exists : do whatever you want here
// if you want to return the result as promise;
deferred.resolve(data);
})
.catch(function(errorData) {
// catch means does not exist : create or do anything
deferred.reject(errorData);
});
} else {
// no id provided
deferred.reject();
}
return deferred.promise();
}
function _isRecordExists(passedId){
var decision;
var baseUrl = "some url";
var dfd = $.ajax({
url: baseUrl,
type: "GET",
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose"
}
});
return dfd.promise().then(
function(data, status, jqXHR){
decision = true;
dfd.resolve(decision);
},
function (jqXHR, status, error) {
decision = false;
dfd.reject(decision);
});
}
}
Syntax errors at function _mainFunction()( , close of _mainFunction() at ) ; jQuery promise object not returned from _helperFunction
function _mainFunction() {
var recordID = prompt("Enter Desired Record ID");
var promise = _helperFunction(recordID);
promise.then(function success(t) {
// resolved
console.log(t)
}, function err(e) {
// rejected
console.log(e)
})
//do some processing when the reocrd is created or updated
}
function _helperFunction(passedId) {
if (passedId) {
// return `_isRecordsExists`
return _isRecordExists(passedId)
.then(function(data) {
return data
}, function err(data) {
return data
})
} else {
// if `passedId` not entered, return rejected deferred
return $.Deferred().reject("no passedID")
}
}
function _isRecordExists(passedId) {
var decision;
var baseUrl = "some url";
// do asynchronous stuff
// var dfd = $.ajax({
// url: baseUrl,
// type: "GET",
// contentType: "application/json;odata=verbose",
// headers: {
// "accept": "application/json;odata=verbose"
// }
// });
var dfd = $.Deferred(function(d) {
setTimeout(function() {
d.resolve()
}, Math.random() * 2000)
});
// return `dfd` promise here
return dfd.promise().then(
function(data, status, jqXHR) {
decision = true;
return decision
},
function(jqXHR, status, error) {
decision = false;
return decision
})
// return dfd.promise();
// do I return decision here for true or false?
}
_mainFunction()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
function _mainFunction() {
var recordID = prompt("Enter Desired Record ID");
var promise = _helperFunction(recordID);
promise.then(...) //do some processing when the reocrd is created or updated
}
function _helperFunction(passedId){
if (passedId) {
if (!_isRecordExists(passedId)) {
// if record doesn't exist, create it.
}
}
}
function _isRecordExists(passedId){
var decision;
var baseUrl = "some url";
var dfd = $.ajax({
url: baseUrl,
type: "GET",
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose"
}
});
dfd.promise().then(
function(data, status, jqXHR){
decision = true;
dfd.resolve();
},
function (jqXHR, status, error) {
decision = false;
dfd.reject();
});
return decision;
// do I return decision here for true or false?
// answer: NO!!, before the value(true or false) is assigned to decision, decision is returned...(Of course, the value may be allocated and then return). The Promise object should return to the place where the actual value is used.
}
}
// here is my answer
function _mainFunction(passedId){
var recordID = prompt("Enter Desired Record ID");
isExistPromise = _isRecordExists(recordID);
isExistPromise.then(function(data){
if (data.isExist) {}
else {}
});
}
function _isRecordExists(passedId){
var decision;
var baseUrl = "some url" + passedId;
return $.ajax({
url: baseUrl,
type: "GET",
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose"
}
});
}
then I want a true/false to be returned to the main function
You can't return true/false to the main function because by the time your code finish execute, the promise is still working and does not have a result yet, hence your _mainFunction does not know if it should return true or false.
What you can do is return a Promise in _mainFunction and then use .then or .fail to do your logic code.
function _mainFunction()(
var recordID = prompt("Enter Desired Record ID");
var promise = _helperFunction(recordID);
promise.then(function (result) {
if (result == true) {
// Do something
} else {
// Do something else
}
})
)
function _helperFunction() {
return $.ajax(...)
.then(function (response) {
if (...) {
return true;
} else {
return false;
}
});
}
From what I observe in your code, I think you should really spend sometime on learning how to work on asynchronous programming in JavaScript.
These are the helpful links that you might want to read:
Asynchronous JavaScript Programming
An introduction to jQuery Deferred / Promise and the design pattern in general
I am trying to stub several ajax calls, but I want to have both beforeSend and success executed, is this possible?
I want something like this:
var stub = sinon.stub(jQuery, "ajax");
stub.onCall(0).yieldsTo("beforeSend").yieldsTo("success", {some: 'data'});
stub.onCall(1).yieldsTo("beforeSend").yieldsTo("success", {other: 'stuff'});
But this skips the 'beforeSend' method.
I know it would be easier to allow ajax to do it's stuff and use sinon's fakeServer, but I can't as I'm testing in Node with a fake browser and it just doesn't work
You could use yieldTo after the call:
var stub = sinon.stub();
stub({
foo: function() {
console.log('foo');
},
bar: function() {
console.log('bar');
}
});
stub.yieldTo('foo');
stub.yieldTo('bar');
I was able to work around this by adding some additional code:
var responses = {};
var executionComplete;
beforeEach(function () {
executionComplete = $.Deferred();
sinon.stub($, "ajax", function (options) {
if (options.beforeSend) {
options.beforeSend();
}
completeAjaxCall(options);
});
});
afterEach(function () {
$.ajax.restore();
});
var completeAjaxCall = function (options) {
var response = findResponse(options.url, options.type);
setTimeout(function () {
if (response.code < 400) {
if (options.dataFilter && response.data) {
response.data = options.dataFilter(JSON.stringify(response.data));
}
if (options.success) {
options.success(response.data);
}
} else {
if (options.error) {
options.error(response.data);
}
}
if (options.complete) {
options.complete();
}
if (response.completeExecution) {
executionComplete.resolve();
}
}, response.serverResponseTime);
};
var findResponse = function (url, type) {
var response = responses[url];
expect(response, url + ' should have a response').to.exist;
expect(response.type).to.equal(type);
delete responses[url];
if (Object.keys(responses).length === 0) {
response.completeExecution = true;
}
return response;
};
var givenResponse = function (response) {
responses[response.url] = response;
};
Then in my test I can use it like this:
it('should do some stuff', function (done) {
//given
givenResponse({serverResponseTime: 4, code: 200, url: '/saveStuff', type: 'POST'});
givenResponse({serverResponseTime: 1, code: 200, url: '/getStuff', type: 'GET'});
//when
$('button').click();
//then
executionComplete.then(function () {
expect(something).to.be.true;
done();
});
});