I am trying to export a function that will let you put in a parameter (trading pair) and it will return the data for that trading pair. This is the code that creates a function that:
Accepts a parameter (trading pair).
Returns the data for that trading pair in the form of a Promise.
Then calls another function which processes that data.
// This function is exported and let's you feed in pairs you want data for - feeling proud.
function pullData(coinPair) {
kc.getTicker({
pair: coinPair
}).then(returnData).catch(console.error)
}
// This function is the callback, which I believe means this function is called and fed the results of the Promise returned from 'kc.getTicker'.
// At first it just logs the data, but now I'll need to find a way to return the data as a usable array. Maybe use the 'return()' function?
function returnData(pairData) {
// Nae Nae
//return pairData;
console.log(pairData);
}
// Finally we export our hard work
exports.pullData = pullData;
I now want to simply have the exported function return the data (and for this data to be usable by a callback function.
// Including important sub modules & config files
const tradeData = require('./example');
var config = require('./config');
// Log the config pairs (Method 1):
function logArrayElements(element, array) {
console.log(element);
}
config.pairs.forEach(logArrayElements);
// Was going to cycle through config pairs. However, I will instead have a base strategy that will be replicated for each pair and run side by side.
//console.log(tradeData.pullData('ETH-USDT'));
tradeData.pullData('ETH-USDT', calculations);
function calculations() {
console.log(pairData);
}
The relevant lines here are the including of the file ('./example') and the pitiful attempt at using the function with a callback down lower.
My goal is to be able to pass something like this:
tradeData.pullData('ETH-USDT', calculations);
function calculations() {
// Calculations and logic here
}
That may involve having '.then' simply return the data. I believe this would allow for a 'calculations' function to use the data after the asynchronous function finishes...
Any answers are greatly appreciated as I haven't been able to find guidance as to what I should do here.
I'm not sure what it is you like pullData to return. you would like it to return only the data? So something like this:
function pullData(coinPair) {
return kc.getTicker({
pair: coinPair
})
.catch(console.error) //.then(returnData)
}
And then you'd like it to receive a calculations function that is performed on the data like this:
function pullData(coinPair,calculations) {
return kc.getTicker({
pair: coinPair
}).then(calculations).catch(console.error)
}
Now the caller can do:
pullData(pair,calculations);
But the caller may ass well do:
pullData(par).then(calculations)
I'm not sure what the benefit here is.
My question may have confused others, as I was genuinely lost as to the proper structure to follow.
Upon review the following day, what I was trying to do was:
Create a function that would request data from an API.
Export this function and allow it to be called in other files.
Allow those other files to pass in their own callbacks in order to perform their desired calculations and use the data appropriately.
In my original question, I did not define a callback as a parameter for the data collecting function. As a result, the function would only ever expect one parameter, namely the 'pair'. I believe it would then call '.then()' once the asynchronous process was finished. However, the function it calls would have to be within the original file.
To remedy this I had to add a function, in this case, named 'calculations', as a parameter in the function and then have '.then()' call that function. This told '.then()' to look for the function that would be passed as the second parameter and then call it once the data had been received from the API.
This now means I can export the function to multiple other files and if I want other functions to use the data I just have to pass those functions in with the call.
Related
I am developing an AngularJS application and found the following behavior.
I have two functions in my service. The first function returns all the categories stored in the database and the second returns one category by its id.
Here is my service:
angular.module('categoriesRepository', [])
.service('categoriesRepository', ['$cordovaSQLite', 'sqliteHelper',
function ($cordovaSQLite, sqliteHelper) {
//this works - returns an array with all categories
this.getAll = function () {
var categories = [];
$cordovaSQLite.execute(sqliteHelper.getDb(),
"SELECT * FROM categories;")
.then(function (res) {
for (var i = 0; i < res.rows.length; i++) {
categories.push(res.rows[i]);
}
});
return categories;
}
//this works not - returns undefined
this.getById = function (id) {
var category;
$cordovaSQLite.execute(sqliteHelper.getDb(),
"SELECT * FROM categories WHERE id = ?;", [id])
.then(function (res) {
category = res.rows[0];
});
return category;
}
}]);
I know that I can use Angulars $q to run functions asynchronously, and use their values when they are done processing.
Why does the getById function return the category directly and the getAll wait until the array is filled?
EDIT
I had the getAll function posted wrong. There is no return statement before $cordovaSQLite.execute
UPDATE:-
After your question is updated.
In the first example your are creating an array first by doing var categories = [];and then returning this array before finishing your async call. When your async call completes it just pushes certain elements into the array thus not destroying the reference to the array (categories ) variable. When it is returned back if you will debug it you will find the function returning an empty array and later when the async call succeeds only then the array will be filled.
In the second example you are creating just a variable and then returning it before the async call finishes. But then the async call is finished you assign the variable to a new value. thus destroying the earlier reference.
Solution:-
Though not a preffered approach to make it work. you will have to maintain the category variable reference. for this you can use angular.copy OR angular extend
So the second part of your code should be like
this.getById = function (id) {
var category;
$cordovaSQLite.execute(sqliteHelper.getDb(),
"SELECT * FROM categories WHERE id = ?;", [id])
.then(function (res) {
angular.copy(res.rows[0], category);
//now the reference to the category variable
//will not be lost
});
return category;
}
Better Practice:-
The way you have been developing this application is wrong. Async calls should not be handled this way. I earlier asked a question just to clarify the way to handle the async calls and state inside the angular app, factories and controllers please have a look here. It provides two ways to handle the state and async calls. There might be many more practices out there but these two suit me best.
It is unfortunate that this approach appears to 'work' because it is caused by the modification of the returned array object "at some unspecified time" after it is returned.
In the usage the array is accessed/observed after1 it has been modified by the asynchronous call. This makes it appear to function correctly only because of the (accidental) asynchronous-later-than observation.
If the observation was prior to the actual completion of the SQLite operation - such as immediately after the getAll function call - it would reveal an empty array.
Both functions are incorrectly written and the first accidently releases Zalgo (or perhaps his sibling).
See How do I return the response from an asynchronous call? for more details.
1 Chrome's console.log can be confusing as it works like console.dir and thus may be showing the current value and not the value when it was invoked.
As stated already, this is a bad approach. You can't use result of your function immediately after it returns.
However I didn't see the answer to your exact question: why do they behave differently?
It happens because with an array you return a reference to an object (type Array). Later on you use same reference to modify contents of the object, i.e. push new items into the array.
However in second function you modify the reference itself. You make you local variable categories point to a new object. Thus old object (reference to which was returned to outer scope) remains untouched. To make it work the same way you should have written
category.row = res.rows[0];
You return the result of the execute in the first case, whereas you return the variable in the second case, which has most likely not been populated yet.
When trying this simple code:
function create_folder(name, parent_ID) {
var BM_folder = "";
chrome.bookmarks.create({title : name, parent_id : parent_ID }, function (new_folder) {
BM_folder = new_folder;
});
console.log("create folder in id : " + BM_folder.id);
return BM_folder.id;
}
I get undefined as output, but when I debug it works fine and I get the real bookmark ID. I have similar problems in more functions, I guess it's the same problem.
EDIT #1: fixed the vars, my real function has full strings, I simply can't post that way.
EDIT #2: thanks Marco Bonelli, is there a way to turn this into sync, so that I'll be able to use normal oop?
There are several problems in your code:
First of all, that function cannot work... you're using a hypen (-), and variable/function names cannot contain hypens in JavaScript, so change it in something else, maybe create_folder or createFolder. That's the same for your variable BM-folder, and parent-ID. Call them BMFolder and parentID.
Secondly, you are creating the object to pass to chrome.bookmarks.create() in the wrong way: parent-ID is both wrong and undefined. You should do: chrome.bookmarks.create({title: name, parentID: parentid}).
Inside your function, you're calling the chrome.bookmarks.create() method, which is asynchronous: this means that the code is processed separately from the body of your function, and when the method has finished working, it will call the callback function, which you provide as second argument. Basically when calling chrome.bookmarks.create() you have to wait until it's finished to continue, because if you try to access the BMfolder.id variable before the callback gets called it will obviously be undefined.
Now, to summarize what I said above, I'll show the right code for to achieve you're trying to:
function createFolder(name, parentid) {
chrome.bookmarks.create({title: name, parentID: parentid }, function (newFolder) {
console.log("Created the folder with ID: " + newFolder.id);
goOn(newFolder);
});
}
function goOn(BMFolder) {
console.log('Here is the folder: ', BMFolder);
// do something...
}
You cannot use return BMFolder.id, because your function is asynchronous, so the only thing you can do to know that the bookmark folder has been created is to call another function to continue. For example, you can name it goOn().
EDIT:
Is there a way to turn this into sync, so that I'll be able to use normal oop?
Unfortunately you cannot turn an asynchronous function into a synchronous one. Chrome extensions' methods are only asynchronous, therefore you have to work on that. By the way, working asynchronously is much more efficient than working synchronously, and you should get used to this programming style, because (as said before) Chrome extensions only work asynchronously, and so do many other JS frameworks and APIs.
I'm a javascript noob, and I don't understand why this works:
$().load('/my/url/', {my:data, more:data}, jsFunc());
function jsFunc()
{
$("#myid").val("yep");
}
But not this:
$().load('/my/url/', {my:data, more:data}, function() {jsFunc()});
function jsFunc()
{
$("#myid").val("yep");
}
I tried an $.ajax instead of $.load with the same result. I will be passing the response data to jsFunc() and that is why I need jsFunc() inside the function. I'm sure it is something simple I'm just not very experienced with javascript. Thanks.
Thanks again for all the help. I decided to use $.post because it works best for the situation but now I'm having trouble with the response data. My code looks like this:
$.post('/my/url/', {my:data, more:data}, function(data) {
var strung = JSON.stringify(data)
var parse = jQuery.parseJSON(strung)
console.log(parse.some);}, 'json');
I'm logging to the console to see what is coming back for now and I will add the callback when I see the correct value logged. The process I got from the jQuery api page, but it will only log undefined. When I change parse.some to parse the console log will display the objects and I can select an element and see the correct key:value pair. Any help would be sweet.
Neither works. The first one appears to work, because you call the function jsFunc immediately, it doesn't wait for any response.
If you create an empty jQuery object using $() and use the load method on that, it won't call the server because there is no element where it can put the result.
To specify the callback function you either use the name of a function:
$('#someElement').load('/my/url/', {my:data, more:data}, jsFunc);
or a function expression:
$('#someElement').load('/my/url/', {my:data, more:data}, function() { jsFunc(); });
The first code block will simply call jsFunc() and return the results as the parameter to the load(..) method, which is odd because that parameter is supposed to be a callback function to fire when the load completes, but that works? The callback syntax is more in keeping with the second example (the one I believe you stated doesn't work).
Answer to my second part:
My returned JSON data consisted of many objects, so I had to specify the index and the key to get the value to return.
using mongojs to access a mongodb database, the following lines retrieve users:
db.users.find({}, function(err, some_users) {});
Users found are stored in some_users parameter.
Then, I would like to use some_users after the db.users.find() call, not only in the anoymous function.
I've tried to play with closures, but without success.
Any idea?
If you want to have access to some_users after the call to find, then you should initialize a variable to hold the result outside of the anonymous function's scope. e.g.
var someUsers;
db.users.find({}, function(err, some_users) {someUsers = some_users});
//Do things with the filled in someUsers
As noted by nnnnnn in the comments, presuming find is an asynchronous call, someUsers will not be immediately filled in after the call to find.
I am using Lawnchair to save data in js and retrieve it back for my mobile app.
I have this in my js file.
$(document).ready(function() {
//Lawchair set up
db = Lawnchair({name: 'db'},function(e){
console.log('storage open');
});
//store the 'username' key in storage
db.save({key:"username", value: "john"});
var name = ""
db.get("username", function(obj){
name = obj.value;
})
alert(name);
});
The problem is I always get "" in the name. I can never set any variable inside the callback function of "get" from Lawnchair object. Am I missing something?
Any help would be appreciated.
Thanks.
The database operation is asynchronous. Put your alert inside the callback to the ".get()" function.
As a general rule, any time you see a JavaScript API like this:
something(param, param, ... , function(result, result, ...) {
// ...
});
it's a good bet that the function may be an asynchronous mechanism, and that the callback function you supply will only be called later when an event actually transpires. In those cases you have to structure your own code such that activities you need to perform after the operation completes are done in code inside the callback.
(It's not always the case; some functional programming APIs for example take functions as arguments.)