Two ajax calls and only one is running - javascript

I have the following http variable which returns an object:
var http = (function(){
var success = null;
var error = null;
var requestInfo = {content: '', status: 0};
var core = {
request: function(options){
var $http = Object.create(http);
sendRequest(options).then(function(){
if(typeof(success) == 'function'){
success(requestInfo.content);
}
}, function(){
if(typeof(error) == 'function'){
error(requestInfo.content);
}
});
return $http;
},
success: function(callback){
success = callback;
return this;
},
error: function(callback){
error = callback;
return this;
}
};
function sendRequest(options){
return new Promise(function(resolve, reject){
var xhttp = new XMLHttpRequest();
var method = options.method.toUpperCase();
xhttp.onreadystatechange = function() {
if(xhttp.readyState == 4){
requestInfo.content = JSON.parse(xhttp.responseText) || xhttp.responseText;
requestInfo.status = xhttp.status;
}
if (xhttp.readyState == 4 && xhttp.status == 200) {
resolve(requestInfo);
}else if(xhttp.readyState == 4 && xhttp.status != 200){
reject(requestInfo);
}else if(xhttp.status >= 400){
reject(requestInfo);
}
};
xhttp.open((method || 'GET'), options.url, true);
var data = options.data || '';
xhttp.setRequestHeader('X-CSRF-TOKEN', document.querySelector('meta[name="csrf-token"]').getAttribute('content'));
if((typeof(data) == 'object' && (Object.keys(data).length > 0) || data.length > 0)){
xhttp.send(JSON.stringify(data));
}else{
xhttp.send();
}
});
}
return core;
})();
If I call it more than once simultaneously, like so:
http.request({url: '/path1'}).success(function(){
alert('success');
});
http.request({url: '/path2'}).success(function(){
alert('success');
});
only one of the items gets passed via ajax the other one does not. What is causing this? I thought that doing Object.create(this) would make each one unique from each other but it doesn't seem to be doing that...

The Object.create() method creates a new object with the specified prototype object and properties.
So you're creating 2 objects with the same prototype, and the data is in the prototype itself. Since the prototype is an object, and it is the same, both resulting objects have the same references for their data objects and functions.
You need to specify the data not in the prototype object, but for each instance instead.
So after you create var $this = Object.create(this);, you must add the data to $this, such as successFunc, otherwise the next call will override this data.

The sucessand error variable used to store your callback functions must be created within your request function to be truly unique per request. However, if you're going to use Promises, I'd suggest simplifying the whole deal using load and error event listeners available to the XMLHttpRequest object (a nice example can be found on MDN's XMLHttpRequest article) and simply passing in your success and failure functions the then method of your Promise instance.
Here's a simplified JSFiddle example using a timeout to simulate a 500 millisecond HTTP request.

The issue was with this line:
var method = options.method.toUpperCase();
I was not setting the method property in the options and it wasn't erroring saying that it couldn't send, it basically just exited the method without warning...
I changed it to this:
var method = (options.method || 'GET').toUpperCase();
and it started to work.

Related

How to use a function as a property of an object inside another function in javascript

I'm trying to create a function that will take object as a parameter and inside the object I want to have an object method just same way jQuery ajax works. My problem now is that I can't get that object method to work.
function myAjax({
type,
url,
success
}) {
var suc = "";
typeof type === "undefined" ? type = "GET" : type = type;
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
suc = this.responseText;
//alert(this.responseText);
}
};
req.open(type, url, false);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded ");
req.send("fname=chijioke&lname=francis");
function success(suc) {}
}
myAjax({
type: "post",
url: "page.html",
success: function(e) {
alert(e);
}
});
I want to do something similar to this jQuery ajax call
$.ajax({
type: "post",
url: "index",
data: {
id: id
},
success: function(feedback) {
alert(feedback);
}
});
I need to get the responseText through the success function but it is not working. I want this to work like that of jQuery ajax success, this is really for learning purpose.
What you are trying to do is called a callback function. Rather than declaring a function inside of your function, you should be calling the function being passed to it.
so instead of
function success(suc) {}
Try doing
success(suc);
https://codeburst.io/javascript-what-the-heck-is-a-callback-aba4da2deced Here's a guide I found on the topic.
what you are trying to do is actually pretty common - callback functions. You pass a function as a parameter and simply call it inside.
In your implementation, you are doing
function success(suc){}
This is actually just defining a method called success.. you need to do actually call the method like this
success(suc);
Also, your line where you check to see if the type is undefined won't typically evaluate to true, as you should be checking if
type === undefined //"undefined" != undefined
One more thing I would like to mention is that you are attempting to call the callback function at the end of your method, but you should actually be calling it in the onreadystatechange so that way it's being called when the request is completed, not when the method is completed.
req.onreadystatechange = function(){
if (this.readyState == 4 && this.status == 200){
suc = this.responseText;
success(suc);
}
};
The "success" method is already declared when you execute myAjax, so do not need to declare it again in myAjax.
Try this:
function myAjax(type, url, success) {
var suc = "";
typeof type === "undefined" ? type = "GET" : type = type;
var req = new XMLHttpRequest();
req.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
suc = this.responseText;
success(suc); //Call to outside method
}
};
req.open(type, url, false);
req.setRequestHeader("Content-type", "application/x-www-form-
urlencoded ");
req.send("fname=chijioke&lname=francis");
}

Changing the URL of a Request object from the Fetch API

Say if I have small function that takes a Request object as an argument, and calls the fetch() API.
Except I always want to append something to the url, such as ?foo=bar. I'm curious what the best way would be to go about that.
Example:
function fetchFoo(request) {
request.url += '?foo=bar';
return fetch(request);
}
The issue I have is that this won't work. The Fetch API specification states that the url property read-only.
Is there a way to work around this? I'm thinking I might need to construct an all-new Request object, but I'm unsure what a clever way is to inherit all the options from the previous Request object.
Note that I am able to override any other property by using this syntax:
var originalRequest = new Request('/url');
var overriddenRequest = new Request(originalRequest, { method: 'POST' });
Although it wasn't clear from the docs, it seems that the second init parameter takes precedence over the values passed via the originalRequest parameter. I just can't seem to come up with a way to do this for the url as well.
You could leverage the keys that are on the Request.prototype to build a new Request object in just a few lines.
function newRequest(input, init) {
var url = input;
if (input instanceof Request) {
url = mutateUrl(input.url);
init = init || {};
Object.keys(Request.prototype).forEach(function (value) {
init[value] = input[value];
});
delete init.url;
return input.blob().then(function (blob) {
if (input.method.toUpperCase() !== 'HEAD' && input.method.toUpperCase() !== 'GET' && blob.size > 0) {
init.body = blob;
}
return new Request(url, init);
})
} else {
url = mutateUrl(url);
}
return new Request(url, init);
}
Note the special case for body discussed in this answer.

AJAX calls inside a loop

So, I have a list of data that I am out putting onto my view, and each list item has an id.
Each of these list items is a bar and I have a document created for each bar that at least one user is going to. For those bars where no users are going, there is no document created.
I need to make an AJAX call for each list item to the database to check
i) If a document exists for that list item
ii) If a document exists, how many users are going according to the document.
I attempted a solution using a while loop, where the update for the while loop was contained in the callback for the AJAX call. Here is the code
function updateAllGoingButtons(){
var i = 0;
var dataToPass = {};
var numButtons = global_data_object.listData.businesses.length;
while(i < numButtons){
dataToPass.button = global_data_object.listData.businesses[i].id;
dataToPass = JSON.stringify(dataToPass);
ajaxFunctions.ready(ajaxFunctions.ajaxRequest('POST', '/update-buttons', dataToPass, function(data){
console.log(i);
i++;
}));
}
}
When I attempted to run the function, I got the error,
Request entity too large
So, is there a better way to go about doing what I am trying to do? Should I use promises? Or is there simply an error in the way I am trying to make the AJAX call from within a while loop?
For reference, here is the ajaxRequest function
'use strict';
var appUrl = window.location.origin;
var ajaxFunctions = {
ready: function ready (fn) {
if (typeof fn !== 'function') {
return;
}
if (document.readyState === 'complete') {
return fn();
}
document.addEventListener('DOMContentLoaded', fn, false);
},
ajaxRequest: function ajaxRequest (method, url, data, callback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
callback(xmlhttp.response);
}
};
xmlhttp.open(method, url, true);
xmlhttp.setRequestHeader('Content-type', 'application/json');
xmlhttp.send(data);
}
};
You should check out the npm library called async, it has an each method that you can do asynchronous calls within. If you use promises, the Promise.all method in Bluebird could be very useful for you.
So, here's how I did the multiple AJAX calls from within a loop. I used this resource https://medium.com/coding-design/writing-better-ajax-8ee4a7fb95f#.d7ymg99mp (Great resource!)
Here's the code
$('.btn-group').find('button').each(function() {
console.log($(this).attr('id'));
dataToPass.button = $(this).attr('id');
var ajax = $.ajax({
url: '/update-buttons',
method: 'post',
data: dataToPass,
dataType: 'json',
}).success(function(data){
console.log(data);
});
});
Essentially, what this does is selects a div with the class 'btn-group' and then iterates over each button within that div using the jQuery each operator. Then simply make an AJAX request and use the success chain callback to access the returned data.

Parsing arguments to functions involving ajax

I am having an issue trying to improve the functionality of my ajax functions by adding arguments to define what to do with the returned result.
So what i am trying to implement is a way to tell the function to append the result to a given element by its id. But i am having difficulty understanding how to add this functionality.
This is my current code:
var ajax = new function(){
var self = this;
self.x = function() {
if (typeof XMLHttpRequest !== 'undefined') {
return new XMLHttpRequest();
}
};
self.send = function(url, callback, method, data, sync) {
var x = self.x();
x.open(method, url, sync);
x.onreadystatechange = function() {
if (x.readyState == 4) {
callback(x.responseText)
}
};
if (method == 'POST') {
x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
}
x.send(data)
};
self.get = function(url, data, callback, sync) {
var query = [];
for (var key in data) {
query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
}
self.send(url + (query.length ? '?' + query.join('&') : ''), callback, 'GET', null, sync)
};
};
I then make an ajax request like so:
//get the html file, and then call a function
ajax.get(dir.layout+'login.html',false,
function(){
elements.addTemplate(data,'parent',true);
});
In Chrome the xhr shows the correct data and contents, so i know that part works. In my elements.loadTemplate function i have these three lines with their actual values:
elements.addtemplate(data,div_id,append){
console.log(data); //shows:
console.log(div_id); //shows string: parent
console.log(append); //shows: true
}
Now the issue is data is blank, when i want it to contain the contents of the HTML file that I just requested (in this case login.html). I am wondering why it would show up as blank and how i can fix it?
your data is undefined because your callback doesn't accept a parameter
try this:
ajax.get(dir.layout+'login.html',false,
function(data){ // <=== data
elements.addTemplate(data,'parent',true);
});

Backbone: Feed JSON in a variable instead of fetching through URL

We are trying to modify an existing script which uses backbone.js to fetch JSON from a URL and render it in a defined way on screen.
Earlier the script was pointing to an external PHP file to fetch the JSON from it.
url: function () {
var ajaxValue = document.getElementById('ajax').value;
if(ajaxValue==0){
return this.options.apiBase + '/liveEvents.json';
} else {
var eventDate = document.getElementById('timestamp').value;
return this.options.apiBase + '/ajax.php?eventDate='+eventDate;
}
},
But now we are trying to omit the requirement of PHP and get JSON purely using Javascript. For this, we created a JS function fetch_data_set(), that returns proper JSON
var ArrayMerge = array1.concat(array2,array3,array4);
return JSON.stringify(ArrayMerge);
So our question is, how can we feed this JSON to backbone instead of using an external URL. Because if we do this (which is obviously wrong):
url: function () {
var ajaxValue = document.getElementById('ajax').value;
if(ajaxValue==0){
var data_set = fetch_data_set();
return data_set;
}
},
It throws error: Error: A "url" property or function must be specified
The main key is to extend Backbone.sync instead of url() method, so you could use this way to fetch your models in any kind of model, and you could do something similar like this link:
https://github.com/huffingtonpost/backbone-fixtures/blob/master/backbone-fixtures.js
Backbone.Model contains a sync() function able to load JSON data from an url. sync() uses the url() function to determine from where it should fetch data. (Note : sync() is called under-the-hood by save(), fetch() and destroy())
The trick here is that you should stop overriding url() and reimplement sync() directly instead, cf. http://backbonejs.org/#Model-sync
Here is an example :
// specialized version to be used with a store.js - like object
sync: function(method, model, options) {
console.log("sync_to_store begin('"+method+"',...) called with ", arguments);
var when_deferred = when.defer();
var id = this.url();
if(method === "read") {
if(typeof id === 'undefined')
throw new Error("can't fetch without id !");
var data = model.store_.get(id);
// apply fetched data
model.set(data);
when_deferred.resolve( [model, undefined, options] );
}
else if(method === "create") {
// use Backbone id as server id
model.id = model.cid;
model.store_.set(id, model.attributes);
when_deferred.resolve( [model, undefined, options] );
}
else if(method === "update") {
if(typeof id === 'undefined')
throw new Error("can't update without id !");
model.store_.set(id, model.attributes);
when_deferred.resolve( [model, undefined, options] );
}
else if(method === "delete") {
if(typeof id === 'undefined')
throw new Error("can't delete without id !");
model.store_.set(id, undefined);
model.id = undefined;
when_deferred.resolve( [model, undefined, options] );
}
else {
// WAT ?
}
console.log("sync_to_store end - Current changes = ", model.changed_attributes());
return when_deferred.promise;
}
Note 1 : API is slightly different from vanilla Backbone since I return
a when promise
Note 2 : url() is still used, as an id

Categories

Resources