.length undefined only after setTimeout - javascript

I am trying to load the results of a GET request from the twitter api in a nodejs file held on AWS. Currently the code runs once and console.log's the feed when loading the file in the terminal but when I use setTimeout to try to refresh the results i get .length undefined.
Currently my code looks like this...
var Twit = require('twit');
var config = require('./config.js');
var T = new Twit(config);
var params = {
exclude_replies: true,
count: 20,
include_entities: false
};
T.get('statuses/home_timeline', params, gotData);
function gotData(err, data, response) {
var timeLine = data;
for (var i = 0; i < timeLine.length; i++) {
var obj = timeLine[i];
console.log(obj.user.name);
};
};
setTimeout(gotData, 10000);
I have reviewed other questions relating to setInterval and .length issues but I've been unable to find anything I was able to apply to this issue
Thanks for any help you can give me

setTimeout(gotData, 10000); invoke gotData without any parameters where as T.get('statuses/home_timeline', params, gotData); invoke it passing retrieved data.
Try following
setTimeout(function() {
T.get('statuses/home_timeline', params, gotData);
}, 10000);

You have to call the GET request again after the timeout, unless the gotdata function is invoked without passing response data. Try something like this.
setTimeout(function(){
T.get('statuses/home_timeline', params, gotData);
}, 10000);

Your goData() function expects three parameters:
function gotData(err, data, response)
But you are calling it without any params in T.get('statuses/home_timeline', params, gotData) and setTimeout(gotData, 10000), that's why you will get this exception because data is undefined in:

Related

Recursive Async function

I have a question about a problem I am bumping into. I am using AngularJS as my framework and do not have access to jQuery nor Lodash.
The problem
I have a function called "refresh". That function makes an async call via angular $http to get new data from the server. The server only gives 25 new updates to me from the date I specify. So to get all the new messages I need to call the server (and update the "updateDate" everytime I get data) until it tells me that it has no more messages (empty array).
Code example
$scope.refresh = function () {
var date = new Date();
$http({
method: 'GET',
url: 'http://path.to.my.server',
timeout: 6000
}).then(function (success) {
date = success.date[0].date; //0 is always the newest message
callback(success.data);
//Do some stuff with the data
}, function (error) {
console.error("Could not retrieve new messages: \n", error.data);
errcallback(error);
});
}
What I have tried
I have tried to get set the request in a separate function and make calls to it like you would do with a normal a-sync function.
I have also tried a while loop and setting a boolean when I am done with collecting. The only problem is that a while loop doesn't wait for the call to end (otherwise it wouldn't be async) and makes quite an impressive loop (not yet infinite, but enough to crash my program).
I was thinking about a for loop, but I do not know how much iterations I should make. It could be 1 but also could also be 5 or more.
I know how recursive functions work, but I do not know how I should use an async recursive function. Any advice or solutions are welcome. (I won't have to be recursive if anyone knows an other solution)
There's nothing particularly special about making async functions recursive, you just don't have to worry about running out of stack.
Just isolate your ajax call into a function, and have that function call itself until it has a complete picture of the data:
$scope.refresh = function () {
var date = new Date();
var results = [];
gather();
function gather() {
$http({
method: 'GET',
url: 'http://path.to.my.server',
timeout: 6000
// presumably using `date` here?
}).then(function(success) {
// This seems to assume you'll always have at least
// one row, which doesn't seem to match with your description
date = success.data[0].date; //0 is always the newest message
if (thereAreNewResults) {
results.push.apply(results, success.data);
gather();
} else {
// We're done
callback(results);
}
}, function (error) {
console.error("Could not retrieve new messages: \n", error.data);
errcallback(error);
});
}
};
That's not meant to be full-formed and perfect, but it should send you in the right direction.
Note my if (thereAreNewResults). I would have thought that would be if (success.data.length) but the code in your question seemed to suggest there'd always be at least one row, so adjust as appropriate.
I will make a recursive function who get the data :
$scope.refresh = function () {
$scope.allDatas = [];
var getData = function(date){
$http({
method: 'GET',
url: 'http://path.to.my.server'+/ date , // should format date her
timeout: 6000
}).then(function (success) {
date = success.date[0].date; //0 is always the newest message
//Do some stuff with the data; all the datas will be available in $scope.allDatas
$scope.allDatas = $scope.allDatas.concat(success.data);
// call again ?
if( /* decide when you stop getting data */ ){
getData(date);
}
}, function (error) {
console.error("Could not retrieve new messages: \n", error.data);
errcallback(error);
});
}
var date = new Date();
// launch the function
getData(date);
}

angularjs getting song with promise

I'm sending data to a server with and JSON object using $http.post().success().error();
The response from the server is returning an id which is a number (example : 51)
After I get the id I'm making another request to the server. This time the data is the id that I got from the previous http call.
second http call looks something like this: $http.get(data= Song_ID ).success().error();
The response from the server has a res.data.url string,
but what the server does is that when the first http call fires, the server is creating a short music sample. This operation of course takes some time, so in the meantime the server sends back the id of the song that is being created
with that id. I want to ask the server if it's finished creating the song already.
If the res.data.url contains a string, it means the song is ready, if not then the operation is still working.
how do I know when the operation has completed?
For now I'm doing an interval that every second sends a http call with the song id and I'm doing an if(res.data.url) { console.log('i have a url for you') }
But sending lots of ajax calls with interval seems wrong to me.
is there a better way to do it?
Should I use angular promise and how?
Here's how it looks like:
function getId() {
// first post to server
var postToServer = function () {
var song_id; // song id
// response first time
var onComplete = function (res) {
song_id = res.data.id;
getMySong(song_id); old stuff
// $location.path("/converting");
return res.data.id;
};
// error first time
var onError = function (res) {
console.log(res);
};
var song_params = songdata.getData()["answers"].toString();
console.log(songdata.getData()["params"]);
var req = {
method: 'POST',
url: 'myserver',
headers: {
'Content-Type': 'application/json'
},
data: angular.toJson({params: "1,1,1,1,1,1"})
};
(function () {
$http(req).then(onComplete, onError);
})();
};
return postToServer();
}
function getMySong(id) {
var interval; // interval
//here is that interval that checks for url
(function () {
interval = $interval(function () {
getUrlSong(id);
}, 1000);
})();
// song url response
var onResSong = function (res) {
console.log(res);
if (res.data.url) {
$interval.cancel(interval);
//$window.location.href = res.data.url;
window.songURL = res.data.url;
songUrl(res.data.url);
}
};
// song url response error
var onErrorSong = function (res) {
console.log(res);
};
// fetch song with the id
var getUrlSong = function (id) {
$http.get('myserver' + id).then(onResSong, onErrorSong)
};
}

Fetch data on different server with backbone.js

I can't see what the problem with this is.
I'm trying to fetch data on a different server, the url within the collection is correct but returns a 404 error. When trying to fetch the data the error function is triggered and no data is returned. The php script that returns the data works and gives me the output as expected. Can anyone see what's wrong with my code?
Thanks in advance :)
// function within view to fetch data
fetchData: function()
{
console.log('fetchData')
// Assign scope.
var $this = this;
// Set the colletion.
this.collection = new BookmarkCollection();
console.log(this.collection)
// Call server to get data.
this.collection.fetch(
{
cache: false,
success: function(collection, response)
{
console.log(collection)
// If there are no errors.
if (!collection.errors)
{
// Set JSON of collection to global variable.
app.userBookmarks = collection.toJSON();
// $this.loaded=true;
// Call function to render view.
$this.render();
}
// END if.
},
error: function(collection, response)
{
console.log('fetchData error')
console.log(collection)
console.log(response)
}
});
},
// end of function
Model and collection:
BookmarkModel = Backbone.Model.extend(
{
idAttribute: 'lineNavRef'
});
BookmarkCollection = Backbone.Collection.extend(
{
model: BookmarkModel,
//urlRoot: 'data/getBookmarks.php',
urlRoot: 'http://' + app.Domain + ':' + app.serverPort + '/data/getBookmarks.php?fromCrm=true',
url: function()
{
console.log(this.urlRoot)
return this.urlRoot;
},
parse: function (data, xhr)
{
console.log(data)
// Default error status.
this.errors = false;
if (data.responseCode < 1 || data.errorCode < 1)
{
this.errors = true;
}
return data;
}
});
You can make the requests using JSONP (read about here: http://en.wikipedia.org/wiki/JSONP).
To achive it using Backbone, simply do this:
var collection = new MyCollection();
collection.fetch({ dataType: 'jsonp' });
You backend must ready to do this. The server will receive a callback name generated by jQuery, passed on the query string. So the server must respond:
name_of_callback_fuction_generated({ YOUR DATA HERE });
Hope I've helped.
This is a cross domain request - no can do. Will need to use a local script and use curl to access the one on the other domain.

Accessing outer scope

I'm working on creating a Users collection with the ability to then grab single users inside. This will be used to match from another system, so my desire is to load the users once, and then be able to fine/match later. However, I'm having a problem accessing the outer users collection from an inner method.
function Users(){
var allUsers;
this.getUsers = function () {
// ajax to that Jasmine behaves
$.ajax({
url: '../app/data/jira_users.json',
async: false,
dataType: 'json',
success: function(data) {
allUsers = data;
}
});
return allUsers;
};
this.SingleUser = function (name) {
var rate = 0.0;
var position;
this.getRate = function () {
if(position === undefined){
console.log('>>info: getting user position to then find rate');
this.getPosition();
}
$.ajax({
url: '../app/data/rates.json',
async: false,
dataType: 'json',
success: function(data) {
rate = data[position];
}
});
return rate;
};
this.getPosition = function () {
console.log(allUsers);
//position = allUsers[name];
return position;
};
//set name prop for use later I guess.
this.name = name;
};
}
and the test that's starting all of this:
it("get single user's position", function(){
var users = new Users();
var someone = new users.SingleUser('bgrimes');
var position = someone.getPosition();
expect(position).not.toBeUndefined();
expect(position).toEqual('mgr');
});
The getPosition method is the issue (which might be obvious) as allUsers is always undefined. What I have here is yet another attempt, I've tried a few ways. I think the problem is how the Users.getUsers is being called to start with, but I'm also unsure if I'm using the outer and inner vars is correct.
Though the others are correct in that this won't work as you have it typed out, I see the use case is a jasmine test case. So, there is a way to make your test succeed. And by doing something like the following you remove the need to actually be running any kind of server to do your test.
var dataThatYouWouldExpectFromServer = {
bgrimes: {
username: 'bgrimes',
show: 'chuck',
position: 'mgr'
}
};
it("get single user's position", function(){
var users = new Users();
spyOn($, 'ajax').andCallFake(function (ajaxOptions) {
ajaxOptions.success(dataThatYouWouldExpectFromServer);
});
users.getUsers();
var someone = new users.SingleUser('bgrimes');
var position = someone.getPosition();
expect(position).not.toBeUndefined();
expect(position).toEqual('mgr');
});
This will make the ajax call return whatever it is that you want it to return, which also allows you to mock out tests for failures, unexpected data, etc. You can set 'dataThatYouWouldExpectFromServer' to anything you want at any time.. which can help with cases where you want to test out a few different results but don't want a JSON file for each result.
Sorta-edit - this would fix the test case, but probably not the code. My recommendation is that any time you rely on an ajax call return, make sure the method you are calling has a 'callback' argument. For example:
var users = new Users();
users.getUsers(function () {
//continue doing stuff
});
You can nest them, or you can (preferably) create the callbacks and then use them as arguments for eachother.
var users = new Users(), currentUser;
var showUserRate = function () {
//show his rate
//this won't require a callback because we know it's loaded.
var rate = currentUser.getRate();
}
var usersLoaded = function () {
//going to load up the user 'bgrimes'
currentUser = new users.SingleUser('bgrimes');
currentUser.getRate(showUserRate);
}
users.getUsers(usersLoaded);
your approach to fill the data in allUsers is flawed
the ajax call in jquery is async so every call to users.getAllUsers would be returned with nothing and when later the success function of the jquery ajax is called then allUsers would get filled
this.getUsers() won't work. Its returning of allUsers is independent from the ajax request that fetches the data, because, well, the ajax is asynchronous. Same with getRate().
You'll have to use a callback approach, where you call getUsers() with a callback reference, and when the ajax request completes, it passes the data to the callback function.
Something like:
this.getUsers = function (callback) {
// ajax to that Jasmine behaves
$.ajax({
url: '../app/data/jira_users.json',
async: false,
dataType: 'json',
success: function(data) {
callback(data);
}
});
};
And the call would be along the lines of:
var user_data = null;
Users.getUsers(function(data) {
user_data = data;
});

Consuming OData Servie using DataJS

I have this javascript code in MVC view i try to call OData Service using Datajs :
$(document).ready(function() {
var temp = OData.read("http://odata.netflix.com/v1/Catalog/Genres", function (data, response) {
var x = 3;
});
});
i have a break-point on var x = 3; but unfortunately the break-point was never hit! if i put break-point on OData.Read i can see its calling the OData-Service but then the callback function never get fired , i dont know what im doing wrong?
Add this before calling OData.Read():
OData.defaultHttpClient.enableJsonpCallback = true;
Also your request doesn't specify format. Change the request string to: http://odata.netflix.com/v1/Catalog/Genres?$format=json&$callback=?callbackHere

Categories