I have an ajax call which return some data. Code is:
function ProductCodeLookUp() {
return $.ajax({
url: "/ProductInventory/GetProductCodeHirerchy",
type: "get", //send it through get method
async: false,
data: {
productCode: $('#ProductCodeText_Id').val()
},
success: function (response) {
debugger;
if (response != null && (response.tissueClassificationType != null || response.tissueClassificationType != null)) {
$('#productCodeTable_id').show();
$('#ProductCodeNotExistsWarning_id').hide();
//addOptionsToSelect(response, 'size_id', $('input[id=size_id]').val());
$('#ProductCodeTextClassification_Id').text(response.tissueClassificationType);
$('#ProductCodeTextTissueType_Id').text(response.tissueType);
$('#ProductCodeTextProduct_Id').text(response.tissueDefinition);
$('#ProductCodeTextSize_Id').text(response.tissueDefinitionSize);
}
else {
$('#ProductCodeNotExistsWarning_id').show();
$('#productCodeTable_id').hide();
}
},
error: function (xhr) {
//Do Something to handle error
}
});
}
I Have to take some decision on the basis of the data returned by this Ajax call. The method where i am calling ProductCodeLookUp() method is:
async function checkRequiredFields() {
var isValid = false;
if (checkRequiredFieldsSubMethod() == true) {
if (AddProductInBulk() == true) {
if (QualityChecksValidation() == true) {
debugger;
var res = await ProductCodeLookUp();
if (res.tissueDefinitionSize != null)
isValid = true;
else
isValid = false;
}
}
}
debugger;
return isValid;
}
Issue is that it does not await on the ProductCodeLookUp() method. I have to wait for the result of this method and on the basis of result make a decision that whether to return true or false. But i am unable to do this. What can be the possible issue.
Wrap the ajax call in a promise that resolves on success and rejects on error.
function ProductCodeLookUp() {
return new Promise((resolve, reject) => $.ajax({
// ...
success: function (response) {
// ...
resolve();
},
error: function (response, status, err) {
// ...
reject(err);
}
}));
}
Also, set async to true
Related
I'm trying to make a Tampermonkey script to update dates on some site.
I got an array of id's from a site, and I'm requesting data from it with the id of the array. After that, I have to return data of each Input.
As the function is async, it returns data in a random order, but I need those new arrays to return in the original order. I have tried sync and Promises, but the first is too slow and I haven't understood the second.
I can sort ids, but I also got the dates which are in the order of the first Array, so I don't know how to achieve the same order as the second id array.
Here's the code:
id = GM_getValue('id');
for (let i = 0; i < id.length; i++) {
setTimeout(() => {
console.log("Updating " + (i + 1) + " Title");
GM_xmlhttpRequest({
method: "GET",
url: "***" + id[i] + "/***",
onload: function(response) {
$(response.responseText).find("#main-form :input").each(function(x) {
if (x == 0) ids.push(parseInt($(this).val()));
if (x == 1) array.push($(this).val()));
});
}
});
}, i * 333);
}
You can use Promises to execute the GET requests in a specific order. Here's an example:
id = GM_getValue('id');
function makeGetRequest(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function(response) {
resolve(response.responseText);
},
onerror: function(error) {
reject(error);
}
});
});
}
for (let i = 0; i < id.length; i++) {
console.log("Updating " + (i + 1) + " Title");
try {
const response = await makeGetRequest("***" + id[i] + "/***");
$(response).find("#main-form :input").each(function(x) {
if (x == 0) ids.push(parseInt($(this).val()));
if (x == 1) array.push($(this).val());
});
} catch (error) { // in case the GET request fails
console.error("Request failed with error code", error.status, ". Message is ", error.responseText);
}
}
In this example, I've created a makeGetRequest() function with returns a promise, which is resolved on GET success, but rejected on failure.
await waits for the Promise to settle before moving on and the try exists to catch Promise rejection (if the GET fails).
References:
Promise on MDN.
await on MDN.
TypeScript:
export enum HttpDataType {
JSON = "JSON"
}
import {HttpDataType} from "./enum/HttpDataType";
export default class Http {
static get(option: { url: string, dataType?: HttpDataType, synchronous?: boolean, onload?: Function }) {
option['method'] = 'GET';
if (option.synchronous) {
return new Promise((resolve, reject) => {
// #ts-ignore
GM_xmlhttpRequest({
...option,
onload: (response) => {
resolve(option.dataType === HttpDataType.JSON ? JSON.parse(response.responseText) : response.responseText);
},
onerror: (error) => {
reject(error);
}
});
})
} else {
const onload1 = function (details) {
let response;
if (option.dataType === HttpDataType.JSON) {
response = JSON.parse(details.responseText);
} else {
response = details.response;
}
option.onload(response);
}
// #ts-ignore
GM_xmlhttpRequest({...option, onload: onload1});
}
}
}
static async getXxx() {
let response = await Http.get({
url: '……',
dataType: HttpDataType.JSON,
synchronous: true
});
// #ts-ignore
if (!response || response.status !== 'success') {
console.error('getXxx error', response);
}
// #ts-ignore
return response.data;
}
this.getXxx().then((data: any) => {
// ……
});
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 have a modified code in react-native for fetching data with server, that works fine. I want to add NetInfo to always check before fetching if telephone has connection to internet. Is it posible inside promise? How to connect this async function to my code?
'use strict';
var MAX_WAITING_TIME = 30000
var processStatus = function (response) {
// status "0" to handle local files fetching (e.g. Cordova/Phonegap etc.)
if (response.status === 200 || response.status === 0 || response.status === 201 || response.status === 422 || response.status === 302 ) {
return Promise.resolve(response)
} else if(response.status === 413) {
return Promise.reject(alert(____mobile.connection_error.large_file))
} else {
//return Promise.reject(alert("Process status: "+JSON.stringify(response )))
return Promise.reject(alert(____mobile.connection_error.top));
console.log("Process status: "+JSON.stringify(response ));
}
};
var parseJson = function (response) {
return response.json();
};
var getWrappedPromise = function () {
var wrappedPromise = {},
promise = new Promise(function (resolve, reject) {
wrappedPromise.resolve = resolve;
wrappedPromise.reject = reject;
});
wrappedPromise.then = promise.then.bind(promise);
wrappedPromise.catch = promise.catch.bind(promise);
wrappedPromise.promise = promise;// e.g. if you want to provide somewhere only promise, without .resolve/.reject/.catch methods
return wrappedPromise;
};
/* #returns {wrapped Promise} with .resolve/.reject/.catch methods */
var getWrappedFetch = function () {
var wrappedPromise = getWrappedPromise();
var args = Array.prototype.slice.call(arguments);// arguments to Array
fetch.apply(null, args)// calling original fetch() method
.then(function (response) {
wrappedPromise.resolve(response);
}, function (error) {
// wrappedPromise.reject(alert("Fetch status: " + error));
wrappedPromise.reject(____mobile.connection_error.top);
console.log("Fetch status: " + error);
})
.catch(function (error) {
wrappedPromise.catch(error);
});
return wrappedPromise;
};
/**
* Fetch JSON by url
* #param { {
* url: {String},
* [cacheBusting]: {Boolean}
* } } params
* #returns {Promise}
*/
var postJSON = function (params) {
var headers1 = {}
if (params.json){
headers1 = {
'Accept': 'application/json',
'Content-Type': 'application/json'}
}
if (params.headersIn){
headers1 = params.headersIn
}
var methodTmp = 'POST'
if (params.methodIn) {
methodTmp = params.methodIn
}
console.log(methodTmp)
var wrappedFetch = getWrappedFetch(
params.cacheBusting ? params.url + '?' + new Date().getTime() : params.url,
{
method: methodTmp,//'POST',// optional, "GET" is default value
headers: headers1,
body: params.send_data
});
var timeoutId = setTimeout(function () {
wrappedFetch.reject(alert(____mobile.connection_error.timeout, ____mobile.connection_error.check_connection));// reject on timeout
}, MAX_WAITING_TIME);
return wrappedFetch.promise// getting clear promise from wrapped
.then(function (response) {
clearTimeout(timeoutId);
return response;
})
.then(processStatus)
.then(parseJson);
};
module.exports = postJSON;
What would be the bast way to implement: NetInfo.isConnected.fetch() so fetched would only worked when there is internet connection?
EDIT:
I want to use:
NetInfo.isConnected.fetch()
Yeah I have to rewrite this code, not to use getWrappedPromise and now I think is good time for it.
EDIT2: Ok I refactored this code fragment, hope its better. Any comments welcome. I tested and I'm not sure if I still need this NetInfo.isConnected.fetch(). Now there is no errors where there is no connection or am I missing something?
New code:
var processStatus = function (response) {
if (response == undefined) {
return null
}
// status "0" to handle local files fetching (e.g. Cordova/Phonegap etc.)
if (response.status === 200 || response.status === 0 || response.status === 201 || response.status === 422 || response.status === 302 ) {
return Promise.resolve(response)
} else if(response.status === 413) {
return Promise.reject(alert(____mobile.connection_error.large_file))
} else {
//return Promise.reject(alert("Process status: "+JSON.stringify(response )))
console.log("Process status: "+JSON.stringify(response ));
return Promise.reject(alert(____mobile.connection_error.top));
}
};
var parseJson = function (response) {
if (response == undefined) {
return null
}
return response.json();
};
var postJSON = function (params) {
var headers1 = {}
if (params.json){
headers1 = {
'Accept': 'application/json',
'Content-Type': 'application/json'}
}
if (params.headersIn){
headers1 = params.headersIn
}
var methodTmp = 'POST'
if (params.methodIn) {
methodTmp = params.methodIn
}
console.log(methodTmp)
var fetchPromise = fetch(params.cacheBusting ? params.url + '?' + new Date().getTime() : params.url,
{
method: methodTmp,//'POST',// optional, "GET" is default value
headers: headers1,
body: params.send_data
})// calling original fetch() method
.then(function (response) {
return response;
}, function (error) {
console.log("Fetch status: " + error);
return fetch
}).then(processStatus)
.then(parseJson);
// timeoutId = setTimeout(function () {
// wrappedFetch.reject(alert(____mobile.connection_error.timeout, ____mobile.connection_error.check_connection));// reject on timeout
// }, MAX_WAITING_TIME);
return fetchPromise
};
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
here is the structure
var invalid = false;
async.series([
function(callback)
{
someAsync.do(something, function(data)
{
if(data == null) invalid = true;
callback();
}
},
function(callback)
{
if(invalid) callback();
someAsync.doSomethingImportant(something, function(data)
{
if(data == null) invalid = true;
callback();
}
], function()
{
if(invalid) doThis();
else doThat();
});
The problem is that the second async function NEEDS that first async has worked successfully.
Sometimes async.series calls the callback() before the statement invalid = true and crashes my app. Also, sometimes it works.
Could you help me?
Thanks!
Your code looks correct, but if you are depended on the result of the first function, you could try using async.waterfall:
async.waterfall([
function(callback)
{
someAsync.do(something, callback);
},
function(data, callback)
{
if(data == null) {
callback(new Error('no data'));
} else {
someAsync.doSomethingImportant(something, callback)
}
},
function (data, callback) {
if (data == null) {
callback(new Error('no data'));
} else {
callback();
}
}
], function(err)
{
if (err) {
doError();
} else {
doThat();
}
});
Problem solved!
Instead of using my own error control, I used it's default:
async.series([
function(callback)
{
someAsync.do(something, function(data)
{
if(data == null) callback(true);
else callback();
}
},
function(callback)
{
someAsync.doSomethingImportant(something, function(data)
{
if(data == null) callback(true);
else callback();
}
], function(invalid)
{
if(invalid) doThis();
else doThat();
});
If you pass callback(err), it will skip all other asyncs and execute the callback.
Instead of:
[function a () { async_1(); },
function b () { async_2(); }]
function c () {}
what you need is
function a () {
async_1(function (data) {
function b (data) {
async_2(function (result) {
function c (success) { console.log(success); }
c(result);
});
}
b(data);
});
}
Now there is no race.
When a finishes, call b, when b finishes, call c.
This can get really ugly, so tools like Promises are used to turn this into:
async_1()
.then(async_2())
.then(c);
It might not really be that clean, if you need to pass data around (and not just wait for each one to be finiahed) in turn)... ...but promises are very powerful for this kind of work.