Trouble working sync with async javascript - javascript

I'm working in my own rss reader using JS, JQuery and PHP for serving the data as JSON. What I'm doing basically is making async calls to my server to get JSONs with the posts, then on 'success' I parse them using a '$.each' and with JQuery load the content in the DOM.
All of this operations were made async, but now I need to call them in a certain order, and when everithin is done THEN calling a function to process the data.
To give you some background on my task, what I'm doing is a query over a small list of RSS sources to get just the very latest post. With them I concat a string and this string is passed to a text-to-speech service.
I've managed to make it work using an arbitrary setTimeout value of 10 seconds, but my goal is to call the function when all the sources have been processed.
This is a basic version of my parser:
function urgent_posts(url) {
$.ajax({
//the location of my server
url: 'myPostServer.php?url=' + encodeURIComponent(url),
dataType: 'json',
success: function(data) {
//do this for each entry in the feed
$.each(data.feed.entries, function(key, value) {
//validate the date to get just the latest post
if (is_urgent(value.publishedDate)) {
//if condition is met save the title
save_urgent_post_title(value.title);
}
});
}
});
}
What I did to 'make it work' was the following:
$('#test_button').on('click',function(){
urgent_posts(source_1);
urgent_posts(source_2);
urgent_posts(source_3);
//and so on...
urgent_posts(source_n);
setTimeout(function(){
text_to_speech(get_urgent_post_string);
},10000);
});
I tried with no result to make use of the deferred object y JQuery like this:
function urgent_posts(url) {
var deferred = $.Deferred();
$.ajax({
//the location of my server
url: 'myPostServer.php?url=' + encodeURIComponent(url),
dataType: 'json',
success: function(data) {
//do this for each entry in the feed
$.each(data.feed.entries, function(key, value) {
//validate the date to get just the latest post
if (is_urgent(value.publishedDate)) {
//if condition is met save the title
save_urgent_post_title(value.title);
}
});
}
});
return deferred.promise();
}
And chaining everything together:
$('#test_button').on('click',function(){
urgent_posts(source_1)
.then(urgent_posts(source_2))
.then(urgent_posts(source_3))
.then(function(){
text_to_speech(get_urgent_post_string);
});
});
I'd apreciatte your comments and suggestions.

First, your deferred object is never resolved. You have to add the deferred.resolve() somewhere. Just after the $.each loop looks like a nice place.
Second, $.ajax already returns a promise. So you can just write this :
return $.ajax({
//the location of my server
url: 'myPostServer.php?url=' + encodeURIComponent(url),
dataType: 'json',
success: function(data) {
//do this for each entry in the feed
$.each(data.feed.entries, function(key, value) {
//validate the date to get just the latest post
if (is_urgent(value.publishedDate)) {
//if condition is met save the title
save_urgent_post_title(value.title);
}
});
}
});

I manage to solve the problem using this article: link
The refactored code looks like this now:
function urgent_posts_feed_1(callback) {
return $.ajax({
//the location of my server
url: 'myPostServer.php?url=' + encodeURIComponent(feed_1),
dataType: 'json',
success: function(data) {
//do this for each entry in the feed
$.each(data.feed.entries, function(key, value) {
//validate the date to get just the latest post
if (is_urgent(value.publishedDate)) {
//if condition is met save the title
save_urgent_post_title(value.title);
}
});
}
});
}
I repeat myself (I know it's not cool to do so) and write the following functions manually setting the url:
urgent_posts_feed_2
urgent_posts_feed_3
urgent_posts_feed_4
...
urgent_posts_feed_n
And finally...
urgent_post_feed_1()
.then(urgent_post_feed_2)
.then(urgent_post_feed_3)
//...
.then(urgent_post_feed_n)
.then(function(){
text_to_speech(get_urgent_post_string);
});
This way it works like a charm. Now I have to figure out how to pass parameters to the function and not interfer with the callback.

Related

shift() and pop() is not a function

So i was making a random quote generator machine project for learning purposes and encountered a error.
I tried looking for it in other answers but couldn't understand/solve it.
here is the JS code:
$('#new').on('click', function(e) {
e.preventDefault();
$.ajax( {
url: 'https://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1&_jsonp=mycallback',
success: function(data) {
console.log(data);
var post = data.shift(); // The data is an array of posts. Grabbing the first one.
$('.author').text(post.title);
console.log(post.title);
$('.quote').html(post.content);
console.log(post.content);
},
cache: false
});
});
For the first console.log it shows data in form of array, So I tried pop and shift functions to extract the data. here is the format of data:
/**/mycallback([{"ID":1640,"title":"Scott Belsky","content":"<p>To envision what will be, you must remove yourself from the constant concern for what already is.<\/p>\n","link":"https:\/\/quotesondesign.com\/scott-belsky\/","custom_meta":{"Source":"<a href=\"http:\/\/the99percent.com\/book\">book<\/a>"}}])
It gave undefined for the next 2 console.log() .
Here is the error:
Uncaught TypeError: data.shift is not a function
and it gave errors on both functions. any help would be appreciated.
Two things:
Don't define the callback function. Leave that to $.ajax by replacing the explicit function name to ?
Set the data type to jsonp
$.ajax( {
url: 'https://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1&_jsonp=?',
dataType:'jsonp',
cache: false,
success: function(data) {
console.log(data);
var post = data.shift();
console.log(post);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>

Prevent duplicate $.ajax calls using result cache

A similar question has been asked before, but I don't believe it overcomes the challenges in this case because my function calls are all together, so please bear with me (I'll delete the question if appropriate).
I have a number of dashboard widgets that each make an $.ajax call, receive a JSON result and then process that to render a Google chart. The widgets can be used multiple times, so there are some duplicated AJAX calls occurring, e.g.
RenderChart('/api/LoginCount?DaysPrevious=7', 'ColumnChart'); // some parameters removed, for brevity
RenderChart('/api/LoginCount?DaysPrevious=7', 'AreaChart');
RenderChart('/api/LoginCount?DaysPrevious=7', 'Table');
The problem is that this generates multiple calls to the same URL, which is extremely wasteful. I saw in the linked question that an object can be used to cache the results, but when I applied this, it didn't seem to work because the second call to RenderChart (immediately after the first) saw there was no data (yet) in the cache, and called the URL again.
My code is:
function LoadDataFromApi(apiUrl) {
return $.ajax({
type: 'GET',
url: apiUrl,
dataType: "json",
success: function (data) { }
});
}
function RenderChart(apiUrl, chartElementId, chartType, chartOptions) {
$.when(LoadDataFromApi(apiUrl)).done(function (data) {
var el = $('#' + chartElementId);
try {
var arrayOfArrays = BuildGoogleArrayFromData(data); // Transform JSON into array of arrays (required by Google Visualization)
$(el).empty();
if (arrayOfArrays.length == 0) { // Data found?
$(el).append('<p class="noData">No data was found.</p>');
} else {
var wrapper = new google.visualization.ChartWrapper({ // alert(wrapper.getChartType()); // wrapper is the only way to get chart type
chartType: chartType,
dataTable: google.visualization.arrayToDataTable(arrayOfArrays, false),
options: chartOptions,
containerId: chartElementId
});
wrapper.draw();
}
}
catch (ex) {
$(el).append('<p class="error">An error occurred: ' + ex.message + '</p>');
}
});
}
Ideally it would be good to cache the arrayOfArrays value, as at this point all additional processing is also complete. However, getting JavaScript to see what other API calls are in progress, and wait for them is where I'm struggling. Is this possible to achieve?
If anyone can handhold me into achieving both I'll put a bonus on the question. I read about promises, but I need to support IE9+.
I can think of making a cache map with URL as its key, and the AJAX request as its value. We can change your LoadDataFromApi function to leverage this cache, and return appropriate AJAX request, if exists, else make a new request.
Following is a snippet of how it can be done.
var requestCache = {};
function LoadDataFromApi(apiUrl) {
if (!requestCache[apiUrl]) {
requestCache[apiUrl] = $.ajax({
type: 'GET',
url: apiUrl,
dataType: "json"
});
}
return requestCache[apiUrl];
}
This way, you can call LoadDataFromApi without any limit, and chain your promise handlers like this:
LoadDataFromApi('http://fake.url')
.then(function(data) {
// use the data in one widget
})
LoadDataFromApi('http://fake.url')
.then(function(data) {
// use this data in another widget
})
// ... and so on
This way the AJAX call for a particular URL will be made only once, and the result will be shared among the promise handlers.

Display search results from API

Hello there I'm trying to create an app to search for recipes. I've tried using the Yummly API and BigOven api, but I can't get either to work.
here is the code i have for bigOven. I can't get any search results to appear in the "results".
$(function() {
$('#searchform').submit(function() {
var searchterms = $("#searchterms").val();
// call our search twitter function
getResultsFromYouTube(searchterms);
return false;
});
});
function getResultsFromYouTube (searchterms) {
var apiKey = "dvxveCJB1QugC806d29k1cE6x23Nt64O";
var titleKeyword = "lasagna";
var url = "http://api.bigoven.com/recipes?pg=1&rpp=25&title_kw="+ searchterms + "&api_key="+apiKey;
$.ajax({
type: "GET",
dataType: 'json',
cache: false,
url: url,
success: function (data) {
alert('success');
console.log(data);
$("#results").html(data);
}
});
}
Can anyone give me instructions on how to do this?? Thank you very much.
The API is returning JSON data, not HTML. I checked the API docs, and JSONP isn't necessary.
However, when you run this code:
$('#results').html(data);
Your code is going to just put the JSON into your HTML, and that isn't going to get displayed properly. You didn't say whether console.log(data) outputs the data correctly, but I'll assume it is.
So, you'll need to transform your JSON into HTML. You can do that programmatically, or you can use a templating language. There are a number of options, including underscore, jquery, mustache and handlebars.
I recommend handlebars, but it's not a straightforward bit of code to add (the main difficulty will be loading your template, or including it in your build).
http://handlebarsjs.com/
It would depend on you which key and values you have to show to your user's and in which manner... For ex. there is even an image link, you could either show that image to your user's or could just show them the image link...
Simple <p> structure of all the key's with there value's
jQuery
$.each(data.Results, function (key, value) {
$.each(value, function (key, value) {
$("#result").append('<p>Key:-' + key + ' Value:-' + value + '</p>');
});
$("#result").append('<hr/>');
});
Your ajax is working, you just need to parse the results. To get you started:
$.ajax({
type: "GET",
dataType: 'json',
cache: false,
url: url,
success: function (data) {
// Parse the data:
var resultsString = "";
for (var i in data.Results){
console.log( data.Results[i] );
resultsString+= "<div>"+data.Results[i].Title+ " ("+data.Results[i].Cuisine+")</div>";
}
$("#results").html(resultsString);
// If you want to see the raw JSON displayed on the webpage, use this instead:
//$("#results").html( JSON.stringify(data) );
}
});
I had created a little recursive function that iterates through JSON and spits out all of the values (I subbed my output for yours in the else condition) -
function propertyTest(currentObject, key) {
for (var property in currentObject) {
if (typeof currentObject[property] === "object") {
propertyTest(currentObject[property], property);
} else {
$('#results').append(property + ' -- ' + currentObject[property] + '<br />');
}
}
}
Then I called it within your AJAX success -
$.ajax({
type: "GET",
dataType: 'json',
cache: false,
url: url,
success: function (data) {
console.log(data);
propertyTest(data); // called the function
}
});
It spits out all of the data in the JSON as seen here - http://jsfiddle.net/jayblanchard/2E9jb/3/

jquery trouble with getJSON call

Got some basic problem again.
I need to modify a function that previously returned a in code written object.
Im now trying to get the object from json through $.getJSON
function getEventData() {
var result = '';
$.getJSON("ajax.php?cmd=getbydate&fromdate=&todate=", function(data) {
result = data;
});
return result;
}
Problem is that result isn't set in the callback function for obvious reasons.
Do you guys have a solution for this?
Edit:
Ok i got an answer that was removed.
I just had to change it abit..
This is the answer that works:
function getEventData() {
var result = '';
url = "ajax.php?cmd=getbydate&fromdate=&todate=";
$.ajax({
url: url,
async: false,
dataType: 'json',
success: function(data) {
result = data;
}
});
return result;
}
You should program your application in an asynchronous way, which means, that you should use callback functions for you application flow, too, or continue in the getJson callback function. You can also make the request synchronously which should then be able to return the value (or at least assign it and block the function till the callback is completed), but this is not recommended at all:
function getEventData() {
var result = '';
result = $.ajax({
url: "ajax.php?cmd=getbydate&fromdate=&todate=",
async: false,
dataType: "json",
data: data,
success: function(data) {
return data;
}
});
return result;
}
Are you sure that the server returns valid json? It will be better to validate it using a tool like jsonlint. Also make sure that application/json is used as content type for the response.

Using Jquery to get JSON objects from local file

I'm trying to get a list of JSON objects (products) from a local file using Jquery and store all the objects in a single array called allItems. The file is co-located in the same directory as the code, and it's called "allItems.json". Here's how I'm doing it now:
function getAllSupportedItems(){
var allItems = new Array();
$.getJSON("allItems.json",
function(data){
$.each(data.items,
function(item){
allItems.push(item);
});
});
return allItems;
}
Based on this example: http://api.jquery.com/jQuery.getJSON/
For getAllSupportedItems to be able to return any items, the AJAX call needs to run synchronously.
getJSON translates to the following asynchronous call:
$.ajax({
url: url,
dataType: 'json',
data: data,
success: callback
});
Asynchronous is the default. You therefore need to explicitly change your request to a synchronous one:
$.ajax({
url: url,
dataType: 'json',
data: data,
success: callback,
async: false
});
An alternative is to rethink the way you use getAllSupportedItems and make it into an asynchronous utility:
function getAllSupportedItems(callback){
$.getJSON("allItems.json",
function(data){
var allItems = [];
$.each(data.items,
function(item){
allItems.push(item);
});
callback(allItems);
// callback(data.items); should also work
});
}
Update
When I initially wrote this answer, jQuery didn't have built-in Deferred support. It is a lot more concise and flexible to do something like this today:
function getAllSupportedItems( ) {
return $.getJSON("allItems.json").then(function (data) {
return data.items;
});
}
// Usage:
getAllSupportedItems().done(function (items) {
// you have your items here
});
How are you using this? If you're expecting the main function ("getAllSupportedItems") to return the array you make, well that won't work. The $.getJSON function is asynchronous, and so the handler won't actually build the array until after the outer function has returned.

Categories

Resources