I have a requirement in which I need to get data by making AJAX calls. First search for data in endPoint1
, if not found then in endPoint2
, if not found then in endPoint3
. But if any of the endPoint
returns data, then there is no need to make further REST calls.
I have achieved the functionality, but the code looks spaghetti.
If possible, please suggest a better approach to reduce this call indentation.
function myAjaxFunction(endPoint) {
// returns promise
}
function checkForData() {
myAjaxFunction(endPoint1)
.then(function (data) {
if (data.d.results.length > 0) { return "Data found"; }
else {
myAjaxFunction(endPoint2)
.then(function (data) {
if (data.d.results.length > 0) { return "Data found"; }
else {
myAjaxFunction(endPoint3)
.then(function (data) {
if (data.d.results.length > 0) { return "Data found"; }
else {
console.info("sorry data not exist");
}
}, function (e) {
console.error()
});
}
}, function (e) {
console.error()
});
}
},
function (data) {
if (data.d.results.length > 0) { return "Data found"; }
else {
return "not found.";
}
});
}
3 Answers 3
I would suggest passing in an array of endpoints so you can have one loop that calls an endpoint and looks at the result without repeating any code:
function checkForData(endPoints) {
let i = 0;
function next() {
if (i < endPoints.length) {
return myAjaxFunction(endPoints[i++]).then(function(data) {
if (data && data.d && data.d.results && data.d.results.length > 0) {
return data.d.results;
} else {
return next();
}
});
} else {
return null;
}
}
return Promise.resolve().then(next);
}
checkForData([endPoint1, endPoint2, endPoint3]).then(function(result) {
// result here
if (result) {
console.log(result);
} else {
console.log("no data found");
}
}).catch(function(err) {
// error here
});
Note: I also put .then()
on the same line as its parent function to avoid an extra level of indent.
-
\$\begingroup\$ @nbi - Any feedback on any of the answers here? \$\endgroup\$jfriend00– jfriend002017年11月05日 05:22:46 +00:00Commented Nov 5, 2017 at 5:22
-
\$\begingroup\$ @Jfriedn00 thanks for answering... Yes it make code lot easier to read and debug. \$\endgroup\$nbi– nbi2017年11月08日 07:22:06 +00:00Commented Nov 8, 2017 at 7:22
If you are targeting an environment that supports async
/await
(translation: non-Internet Explorer) or are using a transpiler, then this is as simple as a try-catch containing a for..of
loop:
async function checkForData() {
try {
for (let endpoint of [1, 2, 3]) {
let data = await myAjaxFunction(endpoint);
if (data.d.results.length > 0) {
return 'Data found';
}
}
console.log('sorry data not exist');
} catch (e) {
console.error('error occurred:', e.message);
}
}
checkForData().then(result => console.log(result));
// mock for myAjaxFunction - wait 1 second and return data for endpoint 2
function myAjaxFunction(endPoint) {
console.log('calling endpoint', endPoint);
return new Promise(resolve => setTimeout(resolve, 1000))
.then(() => ({
d: {
results: endPoint === 2 ? ['a', 'b'] : []
}
}));
}
If you don't have the liberty to use async
/await
, another elegant approach is to make use of Observables. The following uses observables and has the same outcome as what you are attempting to do:
function checkForData() {
var endpoints = [1, 2, 3];
return Rx.Observable.from(endpoints)
.concatMap(myAjaxFunction)
.find(function (data) {
return data.d.results.length;
})
.map(function (found) {
if (found) {
return 'Data found';
}
console.log('sorry data not exist');
})
.toPromise()
.catch(function(error) {
console.error('error occurred:', e);
});
}
checkForData().then(function(result) {
console.log(result);
});
// mock for myAjaxFunction - wait 1 second and return data for endpoint 2
function myAjaxFunction(endPoint) {
console.log('calling endpoint', endPoint);
return new Promise(function(resolve) {
setTimeout(resolve, 1000);
})
.then(function() {
return {
d: {
results: endPoint === 2 ? ['a', 'b'] : []
}
};
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.2/Rx.min.js"></script>
The good thing about promises is that you can chain them, by returning a new promise within the callback. You can also return a value, which is implicity wrapped in Promise.resolve
.
This way, the code will pass either data, or the result of the next myAjaxFunction
along the chain
function checkForData() {
myAjaxFunction(endPoint1)
.then(function (data) {
return data.d.results.length > 0 ? data : myAjaxFunction(endPoint2)
}).then(function(data) {
return data.d.results.length > 0 ? data : myAjaxFunction(endPoint3)
}).then(function(data) {
// Whichever one was found will end up down here
console.log("The data found was", data);
}).catch(function(e) {
console.error(e);
});
}
-
\$\begingroup\$ Thanks for share the inputs... but in this case .. it will execute all the thens. \$\endgroup\$nbi– nbi2017年11月06日 12:36:41 +00:00Commented Nov 6, 2017 at 12:36
-
\$\begingroup\$ @nbi So what if it executes all the thens? It will only call
myAjaxFunction()
until it finds a match and then not call it anymore. \$\endgroup\$JLRishe– JLRishe2017年11月09日 19:04:43 +00:00Commented Nov 9, 2017 at 19:04