How to return value from async method? - asynchronous

I need to get value from asynchronous query. I tried to get my code to return it, but it still works wrong. Firstly it write in console "undefined", then it write "19" - it's correct value. So, where is an error?
My code:
var Back = Parse.Object.extend("Back");
var query = new Parse.Query(Back);
var LastSerial;
query.get("ghxbtU2KSl").then(function(result){
LastSerial=result.get("SerialNumber");
console.log(LastSerial);
return LastSerial
});
console.log(LastSerial);

As someone already mentioned in the comments, the query will be run asynchronously which means Lastserial will not have been assigned a value when you print it to the console. Everything that depends on the value of Lastserial will need be either nested inside the callback or you may take a look at promises in series promises in parse docs I edited your example to visualize what this means:
var Back = Parse.Object.extend("Back");
var query = new Parse.Query(Back);
var LastSerial;
query.get("ghxbtU2KSl").then(function(result){
//async
LastSerial=result.get("SerialNumber");
//this will print the value correctly
console.log(LastSerial);
return LastSerial
});
//this will be run without waiting for the query to finish, hence print undefined
console.log(LastSerial);

You cannot "return" a value from a .then clause in a Promise. The then clause will run sometime in the future after the code that created it has already exited. The way to handle this is call a function from the then clause that processes the returned data.
Alternatively, you can "chain" your Promises.
var Back = Parse.Object.extend("Back");
var query = new Parse.Query(Back);
var LastSerial;
var p0;
var p1;
p0 = query.get("ghxbtU2KSl");
p1 = p0.then(function(result){
//async
LastSerial=result.get("SerialNumber");
//this will print the value correctly
console.log(LastSerial);
return LastSerial
});
p1.then(function(result){
// Do stuff with result, which is LastSerial
console.log(result);
return 0
});
//this will be run without waiting for the query to finish, hence print undefined
console.log(LastSerial);

Related

Firebase cloud functions stop observer from another function

First function observe any process. However, if first function will not send any response, i want to stop observer manually according to second function.When i call second function, observer does not stop in first function. How can i handle this situation ?
exports.startObserverFunc = functions.https.onRequest((req,res) => {
var userFirebaseID = req.query.firebaseID;
var ref = admin.database().ref("Quickplay");
let callback = ref.child(userFirebaseID).on("value",function(snapshot){
if (snapshot.exists()){
// Some Codes
ref.child(userFirebaseID).off("value", callback);
res.status(200).send({
resultName: "ok"
});
}
});
});
exports.stopObserverFunc = functions.https.onRequest((req,res) => {
var userFirebaseID = req.query.firebaseID;
var ref = admin.database().ref("Quickplay");
ref.child(userFirebaseID).off("value");
res.status(200).send({
resultName: "ok"
});
});
You should avoid using observers/listeners like this in Cloud Functions. It will likely not do what you want, given that your code could be running on any number of server instances, based on the load on your functions. Also, two function invocations are definitely not going to be running on the same server instances, so they have no knowledge of each other.
It's almost certainly the case that you just want to use once() to query for a single object just one time, and use that in your response. This is what all the official samples do.

Meteor reactivity in functions, return session.get('variable'), runs everytime session.set('variable') is run?

Ok, let's say I have a client side function that returns a Session variable, eg:
Template.hello.random = function() {
return Session.get('variable');
};
Somewhere else let's say I have a button that does
Session.set('variable', random_number);
Everytime I hit that button, will my Template.hello.random function run? I find it hard to wrap my head around that..
All the Meteor "Magic" comes from Deps.Dependency and Deps.Computation (http://docs.meteor.com/#deps_dependency)
eg
var weather = "sunny";
var weatherDep = new Deps.Dependency;
var getWeather = function () {
weatherDep.depend()
return weather;
};
var setWeather = function (w) {
weather = w;
// (could add logic here to only call changed()
// if the new value is different from the old)
weatherDep.changed();
};
Session mirror's this pattern, but as a key/value store instead of just one value. (Actually Session is just an instance of the ReactiveDict class)
When you have a computation that calls getWeather() the .depend() call links it to the computation - and the .changed() call invalidates that computation.
eg, getting a computation via Deps.autorun()
computaton = Deps.autorun(function(){
var localWeather = getWeather();
console.log("local weather", localWeather);
});
computation.onInvalidate(function(){
console.log("a dependency of this computation changed");
});
console.log("setting weather");
setWeather("abc");
With this infrastructure - we find out that Template helpers are run in a computation - and when the dependencies of the computation are .changed(), it queues up the template to re-render.

angularFireCollection is not returning any data

I have no issues when using implicit updates (angelFire). However I need for some of my data use explicit updating. So I implemented angelFireCollection on the exact same ref I was using previously but despite the console.log explicitly saying that the read was granted and trying it with both with the onloadcallback and without, I don't get data directly into my assigned variable AND once the callback fires I get a strange looking object that DOES contain the data but not in the form I expect. My scope variable ends up with an empty collection. Never gets populated. Here is the code:
var streamController = function ($rootScope, $scope, $log, $location, angularFireCollection, profileService) {
//Wait for firebaseLogin...
$rootScope.$watch('firebaseAuth', init);
function init() {
if ($rootScope.firebaseAuth == false) {
return
};
var refUsers = new Firebase($rootScope.FBURL+'/users/'+$rootScope.uid);
$scope.profile = angularFireCollection(refUsers, function onload(snapshot) {
console.log(snapshot)
});
};
};
myApp.gwWebApp.controller('StreamController', ['$rootScope', '$scope', '$log', '$location', 'angularFireCollection', 'profileService',
streamController]);
}());
Here is what the console.log looks like ( ie; what snapshot looks like ):
>snapshot
T {z: R, bc: J, V: function, val: function, xd: function…}
Here is the earlier message before the snapshot was returned:
Firebase Login Succeeded! fbLoginController.js:16
FIREBASE: Attempt to read /users/529ccc5d1946a93656320b0a with auth={"username":"xxxxxxx#me.com","id":"529ccc5d1946a93656320b0a"} firebase.js:76
FIREBASE: /: "auth.username == 'admin'" firebase.js:76
FIREBASE: => false firebase.js:76
FIREBASE: /users firebase.js:76
FIREBASE: /users/529ccc5d1946a93656320b0a: "auth.id == $user" firebase.js:76
FIREBASE: => true firebase.js:76
FIREBASE:
FIREBASE: Read was allowed.
and finally the desired binding that ends up with an empty array: again from the console:
$scope.profile
[]
Anyone know what I could possibly be doing wrong?? This is like 5 lines of code. Frustrating.
I have put stops in angelFireCollection factory function and can see that the data is getting added to the collection in the callbacks inside that function but my binded variable never gets updated.
UPDATE
Ok experimenting with a plnkr. It seems that angularFireCollection EXPECTS your returning a LIST of items. The snapshot returns properly if you inspect snapshot.val() it will be whatever object structure was stored in firebase. IF you use angularFireCollection it does indeed bind to the variable HOWEVER it turns a non-list object into a garbled mess and you can not access the object user the normal dot operator. This is either a bug or it is a severe limitation of angularFireCollection which will cause me to revaluate how easily I can use firebase as the backend. I can't share my plnkr because it is accessing non-public data but tomorrow if i have time I will create a public firebase with an object store and demonstrate.
Ok. So it appears that indeed angularFireCollection is MEANT to be array based. Which is fine. It would be VERY helpful if the angularFire documentation was updated to make that clear. As such it is not an implicit vs explicit update technique.
For an explicit non-array based approach I have come up with the following code. Had I not been mislead by the documentation I would have gone down this path originally.
var MainCtrl = function($scope, angularFire) {
$scope.test = {};
var _url = 'https://golfwire.firebaseio.com/tmp';
var _ref = new Firebase(_url);
var promise = angularFire(_ref, $scope, 'implicit');
promise.then ( function(data){
$scope.explicit=angular.copy($scope.implicit );
});
}
You then work locally with the 'explicit' copy and when ready just update the 'implicit' by assigning: $scope.implicit = $scope.explicit.
Here is a plnkr: http://plnkr.co/edit/bLJrL1

How to insert documents in a loop?

I am iterating over a moment-range daterange and trying to insert documents. I am getting the following error:
Exception while simulating the effect of invoking '/carpool_events/insert'
Error
Error: Sorting not supported on Javascript code
at Error (<anonymous>)
at Object.LocalCollection._f._cmp (http://localhost:3000/packages/minimongo/selector.js? 5b3e1c2b868ef8b73a51dbbe7d08529ed9fb9951:251:13)
at Object.LocalCollection._f._cmp (http://localhost:3000/packages/minimongo/selector.js? 5b3e1c2b868ef8b73a51dbbe7d08529ed9fb9951:226:36)
at LocalCollection._f._cmp (http://localhost:3000/packages/minimongo/selector.js?5b3e1c2b868ef8b73a51dbbe7d08529ed9fb9951:218:33)
at _func (eval at <anonymous> (http://localhost:3000/packages/minimongo/sort.js?08a501a50f0b2ebf1d24e2b7a7f8232b48af9057:63:8), <anonymous>:1:51)
at Function.LocalCollection._insertInSortedList (http://localhost:3000/packages/minimongo/minimongo.js?7f5131f0f3d86c8269a6e6db0e2467e28eff6422:616:9)
at Function.LocalCollection._insertInResults (http://localhost:3000/packages/minimongo/minimongo.js?7f5131f0f3d86c8269a6e6db0e2467e28eff6422:534:31)
at LocalCollection.insert (http://localhost:3000/packages/minimongo/minimongo.js?7f5131f0f3d86c8269a6e6db0e2467e28eff6422:362:25)
at m.(anonymous function) (http://localhost:3000/packages/mongo-livedata/collection.js?3ef9efcb8726ddf54f58384b2d8f226aaec8fd53:415:36)
at http://localhost:3000/packages/livedata/livedata_connection.js?77dd74d90c37b6e24c9c66fe688e9ca2c2bce679:569:25
This is my loop with the insert. I have tested the loop by just writing to console.log instead of inserting and the loop works fine
'click button.save-addEventDialogue': function(e, tmpl) {
var start = Session.get("showAddEventDialogue_dateRangeStart");
var end = Session.get("showAddEventDialogue_dateRangeEnd");
var dateRange = moment().range(moment(start),moment(end));
var dateLoopIncrement = moment().range(moment(start),moment(start).add('days',1));
console.log(dateRange);
console.log(dateLoopIncrement);
// Loop through the date range
dateRange.by(dateLoopIncrement, function(moment) {
// Do something with `moment`
var dateToSave = dateRange.start;
// Insert the record
Carpool_Events.insert({
owner: Meteor.user().profile.name,
owner_id: Meteor.userId(),
original_owner: Meteor.user().profile.name,
original_owner_id: Meteor.userId(),
declined: 0,
date: dateToSave.toDate()
});
dateToSave.add('days',1);
});
// Clear the Session
Session.set("showAddEventDialogue_dateRangeStart","");
Session.set("showAddEventDialogue_dateRangeEnd","");
// Close the dialogue
Session.set("showAddEventDialogue", false);
}
What is the right way to do this? Thanks.
The error message Sorting not supported on Javascript code is a result of inserting a JavaScript function (!) into a collection -- for example, by doing something like Carpool_Events.insert({x: function () { ... }}); JavaScript functions should not normally go into collections.
Somewhere in your code there is probably a typo where you are not calling a function (for example, writing Meteor.userId on the client instead of Meteor.userId().) My guess would be that in the process of making your code run on the server, you coincidentally fixed or avoided this.
I wasn't able to visually find the problem in your code -- if I'm wrong, to make more progress it would be helpful to have a reproduction.
It looks like the issue occurs when doing a bulk inserts (inserts in a loop) from the client. What I ended up doing was using a Meteor.methods to execute the insert on the server side. This seemed to workaround whatever the issue of doing this on the client is.
I also realized that I don't need to iterate over the dates using moment-range. Instead i just use moment to get the difference in days and iterate over that.
JS code in the client:
'click button.save-addEventDialogue': function (e, tmpl) {
var start = moment(Session.get("showAddEventDialogue_dateRangeStart"));
var end = moment(Session.get("showAddEventDialogue_dateRangeEnd"));
var days = end.diff(start, 'days');
var count = 0;
var dateToSave = moment(start);
// Loop through the date range
for (count; count <= days; count++) {
Meteor.call('bulkInsertCarpoolEvent', Meteor.user(), dateToSave.toDate());
dateToSave.add('days', 1);
};
// Clear the Session
Session.set("showAddEventDialogue_dateRangeStart", "");
Session.set("showAddEventDialogue_dateRangeEnd", "");
// Close the dialogue
Session.set("showAddEventDialogue", false);
}
On the server:
Meteor.startup(function () {
Meteor.methods({
bulkInsertCarpoolEvent: function (user, date) {
return Carpool_Events.insert({
owner: user.profile.name,
owner_id: this.userId,
original_owner: user.profile.name,
original_owner_id: this.userId,
declined: 0,
date: date
});
}
});
});

Meteor collection.insert callback issues

According to the Meteor documentation....
collection.insert(doc, [callback])
callback Function
Optional. If present, called with an error object as the first argument and the _id as the second.
...then later down...
On the server, if you don't provide a callback, then insert blocks until the database acknowledges the write, or throws an exception if something went wrong. If you do provide a callback, insert returns immediately. Once the insert completes (or fails), the callback is called with error and result arguments, same as for methods.
Which is it, error and _id or error and result? I do have Meteor.methods that are firing their callbacks correctly with error, result available to the scope.
I just can't get the callback to work correctly on a collection.insert(doc, [callback])
Either way I can't get my callback to register anything?
function insertPost(args) {
this.unblock;
if(args) {
post_text = args.text.slice(0,140);
var ts = Date.now();
Posts.insert({
post: post_text,
created: ts
}, function(error, _id){
// or try function(error, result) and still get nothing
// console.log('result: ' + result);
console.log('error: ' + error);
console.log('_id: ' + _id); //this._id doesn't work either
});
}
return;
}
What am I doing wrong? I have been up since 2 am coding...6 pm my time zone...I am blurry, so I might (probably) be missing something quite obvious.
Cheers
Steeve
This was a bug, fixed in the next release. Now, if you provide a callback to insert, it will be called with error and result arguments, where result is the ID of the new document, or null if there's an error.
Since this is serverside code you can just do:
var id = Posts.insert({data}); // will block until insert is complete
and the id will be available.

Resources