Meteor async function in method - asynchronous

I read everything concerning this issue and I must admit I am still pretty lost.
I have a payment system which need to do HTTP POST queries to validate payment.
So basically here is my code on the server:
Payment.sendPayment = function(callback){
HTTP.post(..., function(err, result){
if(err) throw new Error('This is an error!');
callback && callback(null);
});
}
With the method as follow:
Meteor.methods({
buy: function(){
Payment.sendPayment(function(err){
if(err) throw new Meteor.Error('buy', err);
});
}
});
It does not work since the return is not in the main function. I tried with wrapAsync:
Meteor.methods({
buy: function(){
var sendPayment = Meteor.wrapAsync(Payment.sendPayment);
console.log(sendPayment());
}
});
Still does not work. I couldn't find any simple example of wrapAsync. I found some stuff concerning Future package but the posts were quite old.
Any idea to do this?
Thank you!

If you want to use futures, here is an example:
var Future = Npm.require('fibers/future');
Meteor.methods({
buy: function(){
var future = Future();
Payment.sendPayment(function(err){
if(err) {
return future.return(err); //return the error
}
return future.return(); //you can return the result here if you want
});
return future.wait();
}
});

Related

javascript : trying to get a synchronous behaviour with async/await and IIFE

in the code below, I want to get msg#1, msg#2, msg#3 in this order. I'm getting right now: msg#1, msg#3, msg#2. thanks for help ! Denys
function timeoutPromise(time) { return new Promise(function (resolve) { setTimeout(function () { resolve(Date.now()); }, time) }) }
function wait(howlong) { return timeoutPromise(howlong * 1000); }
async function doAsync() {
var start = Date.now(), time;
time = await wait(1); console.log('... ' + (time-start)/1000 );
time = await wait(1); console.log('... ' + (time-start)/1000 );
}
console.log('msg#1');
(async () => { await doAsync(); console.log('msg#2'); })();
console.log('msg#3');
async functions are asynchronous!
The function on the penultimate line is going to reach await doAsync();, go to sleep, and the parent function will continue with the next line console.log('msg#3');.
If you want to wait for that async function to finish, you need to await it too.
an answer that I will suggest myself, more a workaround than a true answer..
let's hope we get an even better answer from the community.
(async () => { await doAsync(); console.log('msg#2'); everythingThatFollowsdoAsync(); })();
function everythingThatFollowsdoAsync(){
// let's do here the rest of the code, now that doAsync() is over.
console.log('msg#3');
}
and then I'm getting the expected output:
msg#1
1.002
2.003
msg#2
msg#3

Asynchronous https firebase functions

Should HTTPS functions return asynchronous promises like realtime functions have to?
We haven't been returning in HTTPS functions (just using res.status.send etc), and it looks like firebase/function-samples aren't either. But the documentation is slightly ambiguous https://firebase.google.com/docs/functions/terminate-functions .
This works now in the latest Firebase:
exports.asyncFunction = functions.https.onRequest(async (request, response) => {
const result = await someAsyncFunction();
response.send(result);
});
HTTP functions currently do not respect returned promises - they require a sent result in order to terminate normally. If an HTTP function doesn't send a result, it will time out.
All other types of functions require a returned promise in order to wait for asynchronous work to fully complete.
If you don't have any async work to wait for, you can just return immediately.
These are the three cases outlined in the docs.
After much looking around , this is implementation with a Promise worked for me to return a value from a Google Cloud Function where the function needs to make a third-party asynchronous call :
exports.getSomeAccessToken = functions.https.onCall((data, context) => {
var dataStr = JSON.stringify(data, null, '\t');
console.log('ENTER [getSomeAccessToken], got dataStr: ' + dataStr);
return new Promise((resolve, reject) => {
gateway.clientToken.generate({}, function (err, gatewayResponse) {
var result = {
clientToken: gatewayResponse.clientToken
};
var resultStr = JSON.stringify(result, null, '\t');
console.log("resultStr : " + resultStr);
resolve(result);
});
});
});
Your cloud functions should return"end" with either of the following
res.redirect(), res.send(), or res.end()
What they mean by returning promises, is lets imagine you have a cloud function that updated a node in your realtime database, you would like to complete that work before responding to the HTTP request.
Example code
let RemoveSomething = functions.https.onRequest((req, res) => {
cors(req, res, () => {
// Remove something
DoDatabaseWork()
.then(function (result) {
res.status(200).send();
})
.catch(function (err) {
console.error(err);
res.status(501).send();
});
});
});
Update: Added DoDatabaseWork example.
const DoDatabaseWork = function () {
return new Promise(function (resolve, reject) {
// Remove SomeNode
admin.database().ref('/someNode/').remove()
.then(function (result) {
resolve();
})
.catch(function (err) {
console.error(err);
reject();
});
});
}

Meteor async method call

I am very new to meteor and I have a method in Meteor.methods like :
sendStory(story) {
HTTP.call("GET", "https://offline-news-api.herokuapp.com/stories", function(error, response){
if(error){
console.log("error")
}else{
console.log(response)
var story = story
return story
}
})
}
and then I am calling this on my cliet like:
Meteor.call('sendStory', this.story, function(res){
console.log("some story")
console.log(res)
})
Here it is not printing the res value it is giving undefined and the api call is made at last..
How can I make api call first and then go to callback from api
Thank you ..
Well,
dont use the callback for http call like this:
sendStory(story) {
var story = HTTP.call("GET", "https://offline-news-api.herokuapp.com/stories");
return story;
}
refer here to Meteor Docs
You cannot return from a callback since Meteor methods run within fibers.
You could use a Future:
sendStory(story) {
Future = Npm.require('fibers/future');
var apiFuture = new Future();
HTTP.call("GET", "https://offline-news-api.herokuapp.com/stories", function(error, response){
if(error){
console.error("Error: ", error);
apiFuture.throw(error);
}else{
console.log("Response: ", response);
apiFuture.return(response);
}
});
return apiFuture.wait();
}
and on the client:
Meteor.call('sendStory', this.story, function(err, res){
console.log("some story");
if (err) {
console.error(err);
} else {
console.log("Great! A response from the API: ", res);
}
});
Like guns mentioned, you are returning undefined from server method. The method doesn't wait for the asynchronous function to terminate. Instead it returns.
In the server, I always go with synchronous approach.

Returning data from server method to client after server calls an HTTP API

I am trying to learn Meteor, starting by writing a simple application where the server calls an HTTP API based on user input, processes the information, then returns it to the client to display it.
I am not having much success. I can't seem to return the result from server to client:
if (Meteor.isServer) {
Meteor.methods({
checkTransit: function(method, url, options) {
this.unblock();
return Meteor.http.call(method, url, function(error, result) {
if (error) {
console.log('SERVER ERRR');
console.log(error);
} else {
console.log('SERVER RESULT');
console.log(result);
}
});
}
})
}
if (Meteor.isClient) {
Template.body.events({
"submit .new-task": function(event) {
// Prevent default browser form submit
event.preventDefault();
var text = event.target.text.value;
var method = 'GET';
var url = 'http://sometransitapi.com';
var options = {
headers: {
'accept': 'application/XML',
'content-type': 'application/XML'
}
}
Meteor.call('checkTransit', method, url, options, function (error, result) {
if (error) {
console.log('CLIENT ERRR');
console.log(error);
} else {
console.log('CLIENT RESULT');
var parser;
parser = new DOMParser();
var xmlDoc
xmlDoc = parser.parseFromString(result, "text/xml");
console.log(xmlDoc);
}
})
}
})
}
I can see the results being returned to the result variable at isServer, but I can not pass the result to the xmlDoc variable in isClient. What am I doing wrong? Is this the correct way to structure things for what I want to do in Meteor?
Meteor.http.call is being called asynchronously in your server code (you are passing a callback). The functions in your Meteor.methods object expect to return a value, so you should be calling Meteor.http.call synchronously. Changing your server code to below should do the trick.
if (Meteor.isServer) {
Meteor.methods({
checkTransit: function(method, url, options) {
this.unblock();
// This will throw an exception and return it as the error object
// in your Meteor.call if an error occurs, otherwise it will
// return an empty error object and the result object will be
// the return value from Meteor.http.call
return Meteor.http.call(method, url);
}
})
}
If you want to keep the logic on the server side then #Curtis' answer should help you.
Looking at what you have, I don't see much of a point of getting the server to do the work. The Http api is available everywhere in Meteor. You are just fetching data, which can be solely done on the front end. This would also technically make it faster. Taking your code and moving it around a bit you get the following.
if (Meteor.isClient) {
Template.body.events({
"submit .new-task": function(event) {
// Prevent default browser form submit
event.preventDefault();
var text = event.target.text.value;
var method = 'GET';
var url = 'http://sometransitapi.com';
var options = {
headers: {
'accept': 'application/XML',
'content-type': 'application/XML'
}
}
Http.call(method, url, options, function (error, result) {
if (error) {
console.log('CLIENT ERRR');
console.log(error);
} else {
console.log('CLIENT RESULT');
var parser;
parser = new DOMParser();
var xmlDoc
xmlDoc = parser.parseFromString(result, "text/xml");
console.log(xmlDoc);
}
});
}
});
}

How do I write a Firebase timestamp and write it again to another location

I am using Firebase.ServerValue.TIMESTAMP at one location within the firebase.
After I write it, I would like to also store that same value at another location.
Is there a shortcut to do this? For example, a way to return the timestamp value that was written? Do I have to read the location of the timestamp back out using .once()?
Thanks in advance,
Aaron
A once() would work okay:
var fb = new Firebase(URL);
fb.set( Firebase.ServerValue.TIMESTAMP, function(err) {
if( !err ) {
fb.once('value', function(snap) {
console.log('the timestamp', snap.val());
});
}
});
You could also utilize a transaction, which passes a snapshot to the success method:
var fb = new Firebase(URL);
fb.transaction(function() {
return Firebase.ServerValue.TIMESTAMP;
}, function(err, success, snap) {
if( err ) { console.error(err); }
else { console.log('the timestamp', snap.val()); }
});

Resources