How to chain two ajax requests with promises - javascript

I am having trouble with ajax/promises. I have two ajax requests total, with the second ajax call relying data to be returned by the first ajax call.
My first ajax call finds Latitude, Longitude, and country code of the value of #search.
My second ajax call finds the weather of that city, but the API URL is dependent on the Latitude, Longitude and country code that my first ajax call returns. So the second ajax call can't be started until the first one is finished.
My logic here is that var ajax1 is assigned a promise, and var ajax2 starts after ajax1.then() checks that ajax1's promise is resolved. Then ajax2 runs and returns another promise. Finally ajax2.done starts after it checks that ajax2's promise is resolved, and then starting my successWeatherFunction().
My problem is that ajax2.done is not working, as the console.log("test") is not showing up on the console. The two earlier console.logs, console.log(info) and console.log(weatherApiUrl) are working.
Thanks!
$("#search").keypress(function(event) {
if (event.which === 13) {
var searchCity = $("#search").val();
var jsonURL = "http://autocomplete.wunderground.com/aq?query=" + searchCity + "&cb=?"
var ajax1 = $.getJSON(jsonURL);
var ajax2 = ajax1.then(function(data) {
var info = [];
info.push(data["RESULTS"][0]["name"]);
info.push(data["RESULTS"][0]["c"]);
info.push(data["RESULTS"][0]["lat"]);
info.push(data["RESULTS"][0]["lon"]);
console.log(info);
var searchLat = info[2];
var searchLng = info[3];
var countryCode = info[1];
if (countryCode === "US") {
var weatherApiUrl = "https://api.forecast.io/forecast/{APIKEY}/" + searchLat + "," + searchLng + "?exclude=minutely" + "&callback=?";
} else {
var weatherApiUrl = "https://api.forecast.io/forecast/{APIKEY}/" + searchLat + "," + searchLng + "?exclude=minutely" + "?units=si" + "&callback=?";
console.log(weatherApiUrl);
}
return $.getJSON(weatherApiUrl);
});
ajax2.done(function(data){
console.log("test");
successCityWeather(data);
});

Your code use then and done. done is the old promises jQuery syntax so you should use only then.
The following code works for me :
$(function() {
$.get('/test').then(function() {
console.log('First request end');
return $.get('/test');
}).then(function() {
console.log('second request end');
});
});
But in your case, maybe a one of your request fail. Give a second parameter to then to log the error :
$.getJSON('...').then(function(data) {
console.log('success', data);
}, function(data) {
console.log('fail', data);
});

If not sure, always use always() handler. That way you will know if the request actually finished with error or not at all.
$.ajax( ...params... )
.always(function(jqXHR, textStatus) {
if (textStatus != "success") {
alert("Error: " + jqXHR.statusText); //error is always called .statusText
} else {
alert("Success: " + jqXHR.response); //might not always be named .response
}});

$.post(jsonURL)
.then(function (data) {
var info = [];
// some actions
return $.getJSON(weatherApiUrl);
})
.then(function(data, status, promise) {
// some actions
successCityWeather(data);
})

Related

Ajax call inside forEach loop Angular

I am having an Array that contains objects in AngularJS. Based on the value of a property (snooz) of these objects I have to call a POST request (getData.sonnzeUpdate()). After going through each object in the array, finally, I have to call a GET request. My issue is the GET request (inside the function $scope.getTableData) is executed before getting the response (res in .then(function(res){}) of the POST request.
I have tried with angular.forEach() and $q.
Here is my code sample
var notifiedAlarms = [];
var d = new Date();
var checkTine = d.getHours() + "-" + d.getMinutes() + "-" + "00";
angular.forEach(snoozedData, function (snoozed_asset, asset_key) {
if (snoozed_asset.snooz == checkTine) {
var data = {};
snoozed_asset.snooz = '';
data.data = snoozed_asset;
var deferred = $q.defer();
getData.sonnzeUpdate(data).then(function (res) {
if (res.status == '200') {
toastr.info('Alarm with property ' + data.data.actualFailureArea + ' is activated');
// $scope.getTableData(); //donot want to call it here. as same call will for multiple time
notifiedAlarms.push(deferred.promise);
} else {
// console.log('Error in update');
}
});
} else {
// no matching snooz
}
});
$q.all(notifiedAlarms).then($scope.getTableData());
In your code, notifiedAlarms is empty at that point: $q.all(notifiedAlarms), because you add the promises to that Array, after they have finished.
And avoid the Deferred antipattern. getData.sonnzeUpdate() already returns you a Promise.
angular.forEach(snoozedData, function (snoozed_asset, asset_key) {
if (snoozed_asset.snooz != checkTine) return;
snoozed_asset.snooz = '';
notifiedAlarms.push(
getData.sonnzeUpdate({ data: snoozed_asset }).then(function(res) {
if (res.status == '200') {
toastr.info('Alarm with property ' + data.data.actualFailureArea + ' is activated');
} else {
throw new Error('Error in update');
}
})
);
});
With $q.all(notifiedAlarms).then($scope.getTableData()) you're executing $scope.getTableData() directly instead of telling it to call it once promises are resolved. Hence, it gets executed earlier than you want.
Change it to $q.all(notifiedAlarms).then($scope.getTableData); to get desired behavior.
Here's an example elaborating this:
jsfiddle example
Notice how in the fiddle, I have both ways,
myownservice.UpdateSomeData().then($scope.setValue)
myownservice.UpdateSomeData().then($scope.setValue2())
And, in HTML only value gets updated and not value2

Recursive promise not returning expected value

With a slight modification, I am attempting to use the code provided by Bergi in jQuery Recursive AJAX Call Promise. In my case I make an AJAX call to test if a username is already used. If it is already in use then compose a new username and test that one. Once we have a username that is not in use then we are done and return that unused username. However, I am not getting the expected return value. The return value I get is undefined. The console log statement:
console.log("Return => " + username);
just before the return from the requestUsername function shows that I am returning a good value, but it is not making it to the:
requestUnused().done(function(unused_uname)
statement. Here is my code:
$(document).ready(function() {
function request(query_val) {
// return the AJAX promise
return $.ajax({
url: "/php/is_dup_ad_json.php",
method: 'GET',
dataType: 'json',
data: {
query: query_val, sid: Math.random()
},
});
}
function requestUsername(username) {
console.log("Initial => " + username);
return request(username).then(function(ajax_json){
$.each(ajax_json, function(key, value) {
$.each(value, function(k, v) {
if ((k == "duplicate") && (v > 0)) {
// try again with a different username
var first_initial = fname.substr(0,1);
var surname = lname.substr(0,6);
var idx = v + 1;
var tmpUname = surname + first_initial + idx;
console.log("Temp => " + tmpUname);
return requestUsername(tmpUname);
}
else {
console.log("Return => " + username);
return username;
}
});
});
});
}
function requestUnused(){
var fname = "bugs";
var lname = "bunny";
var first_initial = fname.substr(0,1);
var surname = lname.substr(0,7);
var init_uname = surname + first_initial;
return requestUsername(init_uname);
}
$("#test").on('click', function() {
requestUnused().done(function(unused_uname) {
console.log("Done => " + unused_uname);
});
});
});
Without debugging tools at hand, I would guess that the return value from "requestUnused()," which is a ".then" returned from "requestUsername" is competing with the ".done". I believe ".done" and ".then" serve a similar purpose. If you want to keep a modular approach, separating the functions as it were, you could define the function in the ".then" externally and remove "requestUsername" entirely. Then (no pun intended) call "request" directly in "requestUnused," applying the ".then" functionality extracted previously in the ".click" function instead of ".done."
Alternatively, you could simply call "requestUnused()" in the click function without a ".done".

Javascript multiple promises not working with q and request middle

So this is my dilemma. I have a list of movies, witch I have scraped from a website, then I want to add additional properties to my newly constructed object(json)
Now the omdi api witch I am using supports searching for a movie by title.
Then I make a get request using request and q middlewares. When I receive information from omdb api in the call back I add that data to the object.
Now the next part is where my problem lies. Now I want to return a new Request using data from the previous request. Now I make an new get Request and return it but then() func isin't returning anything. But I don't seem to realize what I am doing wrong.
Here is my code..
var promises = [];
films.forEach(function (film) {
// Get omdbapi information
promises.push(HttpService.getContent(configExternal.omodburl + '?t=' + film.title.trim() + '&y=' + film.year + '&plot=true&tomatoes=true&r=json').then(function (data) {
var result = JSON.parse(data);
if(Boolean(result.Response) === true) {
film.omdb.push(result);
}
var imdbid = result.imdbID;
return HttpService.getContent(configExternal.themoviedburl + imdbid + '/videos?api_key=' + configExternal.themoviedbkey);
}).then(function(data) {
film.trailers = [];
film.trailers.push(JSON.parse(data));
}).catch(function (err) {
logger.error().info('Error getting ' + film.title + ' from omdb, ErrorMessage : ' + err);
}));
});
//--------------------------------
// When all promises have finished
//--------------------------------
Promise.all(promises).then(function (data, err) {
// do stuff with the data
});
And here is my getContent func
var Service = {
getContent: function(url) {
var deferred = q.defer();
request(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
deferred.resolve(body);
} else {
deferred.reject(error);
}
});
return deferred.promise;
}
};
Problem solved. There wasn't anything wrong with the request as Roamer said. But the moviedata base limits by 40 request per 10 sek witch I didn't know :)

Adding more than one AJAX call to a Script

I need to add a function to my script that gets the current server time.
I use the PHP file below to get the server time in milliseconds.
<?php
date_default_timezone_set('Europe/London');
$serverTime = round(microtime(true) * 1000);
echo json_encode($serverTime);
?>
Then i would like to add an Ajax request to 'get' serverTime.PHP and put it into a variable so that i can calculate the time before something ends correctly.
I currently get the clients time by using this
var now = new Date ().getTime();
Now i want to remove that line and add my ajax request.
I have tried adding the following code into the script but i can not get it to execute
function now ()
{
this.ajax.open('GET', 'serverTime.php',
true);
this.ajax.send(null);
if (this.ajax.readyState != 4) return;
if (this.ajax.status == 200)
{
// get response
var now = eval ('('+this.ajax.responseText+')');
}
}
The end result being the variable 'NOW' contains the output of serverTime.PHP
Here is my script, i have tried to add anothert ajax get request in various ways but i cant get it to function correctly.
$.ajaxSetup({
type: 'GET',
headers: { "cache-control" : "no-cache" }
});
var PlayList = function (onUpdate, onError)
{
// store user callbacks
this.onUpdate = onUpdate;
this.onError = onError;
// setup internal event handlers
this.onSongEnd = onSongEnd.bind (this);
// allocate an Ajax handler
try
{
this.ajax = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{
// fatal error: could not get an Ajax handler
this.onError ("could not allocated Ajax handler");
}
this.ajax.onreadystatechange = onAjaxUpdate.bind(this);
// launch initial request
this.onSongEnd ();
// ------------------------------------------
// interface
// ------------------------------------------
// try another refresh in the specified amount of seconds
this.retry = function (delay)
{
setTimeout (this.onSongEnd, delay*5000);
}
// ------------------------------------------
// ancillary functions
// ------------------------------------------
// called when it's time to refresh the playlist
function onSongEnd ()
{
// ask for a playlist update
this.ajax.open('GET', 'playlist.php', // <-- reference your PHP script here
true);
this.ajax.send(null);
}
// called to handle Ajax request progress
function onAjaxUpdate ()
{
if (this.ajax.readyState != 4) return;
if (this.ajax.status == 200)
{
// get response
var list = eval ('('+this.ajax.responseText+')');
// compute milliseconds remaining till the end of the current song
var start = new Date(list[0].date_played.replace(' ', 'T')).getTime();
var now = new Date ( ).getTime();
var d = start - now + 6500
+ parseInt(list[0].duration);
if (d < 0)
{
// no new song started, retry in 3 seconds
d = 3000;
}
else
{
// notify caller
this.onUpdate (list);
}
// schedule next refresh
setTimeout (this.onSongEnd, d);
}
else
{
// Ajax request failed. Most likely a fatal error
this.onError ("Ajax request failed");
}
}
}
var list = new PlayList (playlistupdate, playlisterror);
function playlistupdate (list)
{
for (var i = 0 ; i != list.length ; i++)
{
var song = list[i];
}
{
document.getElementById("list0artist").innerHTML=list[0].artist;
document.getElementById("list0title").innerHTML=list[0].title;
document.getElementById("list0label").innerHTML=list[0].label;
document.getElementById("list0albumyear").innerHTML=list[0].albumyear;
document.getElementById("list0picture").innerHTML='<img src="/testsite/covers/' + list[0].picture + '" width="170" height="170"/>';
document.getElementById("list1artist").innerHTML=list[1].artist;
document.getElementById("list1title").innerHTML=list[1].title;
document.getElementById("list1label").innerHTML=list[1].label;
document.getElementById("list1albumyear").innerHTML=list[1].albumyear;
document.getElementById("list1picture").innerHTML='<img src="/testsite/covers/' + list[1].picture + '" width="84" height="84"/>';
document.getElementById("list2artist").innerHTML=list[2].artist;
document.getElementById("list2title").innerHTML=list[2].title;
document.getElementById("list2label").innerHTML=list[2].label;
document.getElementById("list2albumyear").innerHTML=list[2].albumyear;
document.getElementById("list2picture").innerHTML='<img src="/testsite/covers/' + list[2].picture + '" width="84" height="84"/>';
document.getElementById("list3artist").innerHTML=list[3].artist;
document.getElementById("list3title").innerHTML=list[3].title;
document.getElementById("list3label").innerHTML=list[3].label;
document.getElementById("list3albumyear").innerHTML=list[3].albumyear;
document.getElementById("list3picture").innerHTML='<img src="/testsite/covers/' + list[3].picture + '" width="84" height="84"/>';
document.getElementById("list4artist").innerHTML=list[4].artist;
document.getElementById("list4title").innerHTML=list[4].title;
document.getElementById("list4label").innerHTML=list[4].label;
document.getElementById("list4albumyear").innerHTML=list[4].albumyear;
document.getElementById("list4picture").innerHTML='<img src="/testsite/covers/' + list[4].picture + '" width="84" height="84"/>';
$('.nowPlayNoAnimate').each(function() {
$(this).toggleClass('nowPlayAnimate', $(this).parent().width() < $(this).width());
});
}
}
function playlisterror (msg)
{
// display error message
console.log ("Ajax error: "+msg);
//retry
list.retry (10); // retry in 10 seconds
}
Why not use the jquery method ?
function getServerTime() {
var now = null;
$.ajax({
url: "serverTime.php",
dataType: 'JSON',
async: false,
success: function (data) {
now = data;
}
});
return now;
}
You can start as many requests as you want in a browser-portable way.
PS:
you may also want to replace
document.getElementById("list4artist").innerHTML=list[4].artist;
by the shorter
$("#list4artist").html(list[4].artist);
Edit: Add async parameter to make the execution wait for the ajax call to simulate a non-asynchronous function call.
Assuming your service return the date object, you need to add the following line inside the success function (suggested by Clément Prévost):
var now = data;
The success function is an async callback function the triggers after the server return a value.
You should read about Jquery Ajax, it will make your life much easier.

Parse cloud code loop through url and httpRequest

I just want to do a simple loop in my "alerts" objects, which contains an url, and a word.
For each alert, I do a httpRequest to check if the word is present in the response html code. I yes, I put the status to true.
I also want to update each time the "updatedTo" column, even if I don't find the word in the response html code, but I don't know why...
I wrote this cloud code, but it don't works, or it works sometimes only if I have only items with the word present.
Parse.Cloud.job("updateStatus", function(request, status) {
Parse.Cloud.useMasterKey();
var counter = 0;
var AlertItem = Parse.Object.extend("Alert");
var query = new Parse.Query(AlertItem);
query.each(function(alert) {
var alertTitle = alert.get("title");
var alertUrl = alert.get("url");
var alertStatus = alert.get("status");
var alertWords = alert.get("research");
console.log("Alert : " + alertTitle + " - Check if : " + alertWords + " is on : " + alertUrl)
promise = promise.then(function() {
return Parse.Cloud.httpRequest({
url: alertUrl,
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25'
},
}).then(function(httpResponse) {
console.log("We succeded to access to the website");
var htmlCode = httpResponse.text;
if (htmlCode.indexOf(alertWords) >= 0) {
if (alertStatus == false) {
alert.set("status", true);
console.log("new status:true");
return alert.save();
}
} else {
alert.set("status", false);
console.log("new status:false");
//I do this to updated the "updatedTo" field, but it doesn't work
return alert.save();
}
// You need to return a Promise here if non of the above condition meet.
},
function(error) {
console.error('Request failed with response code ' + httpResponse.headers.Location);
// You need to return a rejected promise here.
}
});
});
return promise;
}).then(function() {
status.success('Status updated');
// Set the job's success status
}, function(error) {
// Set the job's error status
status.error("Uh oh, something went wrong.");
});
});
The query.each(callback, options) from documentation.
Iterates over each result of a query, calling a callback for each one. If the callback returns a promise, the iteration will not continue until that promise has been fulfilled. If the callback returns a rejected promise, then iteration will stop with that error. The items are processed in an unspecified order. The query may not have any sort order, and may not use limit or skip.
Parse.Cloud.job("updateStatus", function(request, status) {
Parse.Cloud.useMasterKey();
var counter = 0;
var AlertItem = Parse.Object.extend("Alert");
var query = new Parse.Query(AlertItem);
query.each(function(alert) {
var alertTitle = alert.get("title");
var alertUrl = alert.get("url");
var alertStatus = alert.get("status");
var alertWords = alert.get("research");
console.log("Alert : " + alertTitle + " - Check if : " + alertWords + " is on : " + alertUrl)
return Parse.Cloud.httpRequest({
url: alertUrl,
headers: {
'user-agent': 'A user classic agent'
},
success: function(httpResponse) {
console.log("We succeded to access to the website");
var htmlCode = httpResponse.text;
if (htmlCode.indexOf(alertWords) >= 0) {
if (alertStatus == false) {
alert.set("status", true);
console.log("new status:true");
return alert.save();
}
} else {
alert.set("status", false);
console.log("new status:false");
//I do this to updated the "updatedTo" field, but it doesn't work
return alert.save();
}
// You need to return a Promise here if non of the above condition meet.
},
error: function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.headers.Location);
// You need to return a rejected promise here.
}
});
}).then(function() {
status.success('Status updated');
// Set the job's success status
}, function(error) {
// Set the job's error status
status.error("Uh oh, something went wrong.");
});
});
So, with any help it was difficult, but I finish to find another post who was close to what I need, I adapt it, and I success to use it, it works great with Promises :) :
var _ = require('underscore.js')
Parse.Cloud.job("updateStatus", function(request, response) {
var alerts = Parse.Object.extend("Alert");
var query = new Parse.Query(alerts);
query.equalTo("status", false);
query.find().then(function(alerts) {
var promise = Parse.Promise.as();
_.each(alerts, function(alert) {
var alertUrl = alert.get("url");
...
promise = promise.then(function() {
return Parse.Cloud.httpRequest({
url: alertUrl
}).then(function(httpResponse) {
...
},
function(error) {
...
});
});
});
return promise;
}).then(function() {
response.success("All status updated with success !");
},
function (error) {
response.error("Error: " + error.code + " " + error.message);
});
});

Categories