Meteor: Proper Use of Meteor.Wrapasync on Server

Meteor: Proper use of Meteor.wrapAsync on server

From the Meteor.wrapAsync http://docs.meteor.com/#meteor_wrapasync you can see that you need to pass it a function and optionally a context, whereas on your two attempts you are passing the RESULT of calling the async version of Stripe.customers.create.

Meteor.methods({
stripeCreateUser: function(options) {
// get a sync version of our API async func
var stripeCustomersCreateSync=Meteor.wrapAsync(Stripe.customers.create,Stripe.customers);
// call the sync version of our API func with the parameters from the method call
var result=stripeCustomersCreateSync({
description: 'Woot! A new customer!',
card: options.ccToken,
plan: options.pricingPlan
});
// do whatever you want with the result
console.log(result);
}
});

Meteor.wrapAsync transforms an async function into a convenient synchronous-looking function which allows to write sequentially looking code. (underhood everything is still executed within the asynchronous Node.js event loop).

We need to pass to Meteor.wrapAsync our API function (Stripe.customers.create) along with the function context, ie this inside the body of the API func, which in this case is Stripe.customers.

EDIT :

How to retrieve errors ?

Traditional node style API functions usually take a callback as last argument that will get called ultimately when the required task is completed. This callback takes 2 arguments : error and data, either one will be null according to the result of the call.

How do we access the error object using the synchronous wrapped functions returned by Meteor.wrapAsync ?

We have to rely on using try/catch blocks, because in case of error, it will be thrown by the sync function instead of being passed as first argument of the async function callback.

try{
var result=syncFunction(params);
console.log("result :",result);
}
catch(error){
console.log("error",error);
}
// is the equivalent of :
asyncFunc(params,function(error,result){
if(error){
console.log("error",error);
return;
}
console.log("result :",result);
});

why doesn't Stripe need to be passed?

JavaScript has no "namespace" concept, so API developers use the common trick of defining a global object acting as API namespace, properties defined on this object are "sub-modules" of the API.
It means that Stripe.customers is a submodule of the Stripe API exposing customers-related funcs, and as such these funcs this context is Stripe.customers, not Stripe.

You can test it yourself by copy pasting this mocking code in your browser console :

Stripe={
customers:{
create:function(){
console.log(this==Stripe.customers);
}
}
};

And then calling the stub function in your browser console like this :

> Stripe.customers.create();
true

Using Meteor.wrapAsync correctly

You don't need to use external callback to sync methods as Meteor supports "async" and "awaits" by default. Below is an example of using 'await' method.

Meteor.methods({
async emailSend(fromAddress, subject, emailText) {
const { Email } = require('../server/email.js');
var sendEmailReturn = await Email.send(fromAddress, subject, emailText);
}
});

Dealing with Meteor.Error and .wrapAsync() - best methods?

@faceyspacey's way of doing it is the best way in my opinion. If you look at the source code for wrapAsync, it's very similar (https://github.com/meteor/meteor/blob/832e6fe44f3635cae060415d6150c0105f2bf0f6/packages/meteor/helpers.js#L90). The method is also used in several external packages.

How can I execute a callback on a meteor method that does async calls?

What you are doing now is passing a function to the server. If that does work, it's very insecure. What you want to do is create a future and then use it to manage the asynchronous function. Here is an example:

let Future = Npm.require('fibers/future');
Meteor.methods({
someRequest: function (someArg) {
// for security reasons, make sure you check the type of arguments.
check(someArg, String);

// future is an async handler.
let future = new Future();
// this is a function for a generic node style callback [ie, function (err, res)]
let resolver = future.resolver();

// run your function and pass it the future resolver
// the future will be resolved when the callback happens.
anAsyncFunction(someArg, resolver);

// this meteor method will not end until future has been resolved.
return future.wait();
}
});

Alternatively, Meteor provides a wrapAsync that provides similar functionality of wrapping async functions in futures so they can run in meteor methods. That is:

let wrappedAsyncFunction = Meteor.wrapAsync(anAsyncFunction /** second argument is `this` binding*/);
return wrappedAsyncFunction();

Meteor wrapAsync executes synchronously but never returns

The function you are wrapping takes three arguments, but you are only providing two: url, and (implicitly) the callback function (I'll call it cb). So internally, what will get executed is Filepicker.prototype.stat(url, cb), i.e., the callback function cb will get interpreted as the options rather than the callback, and callback will be set to an empty function. So the callback of wrapAsync is never called, because the callback chain was broken.

This should work:

result = filepickerStatSync(url, {});

Meteor: Calling an asynchronous function inside a Meteor.method and returning the result

Andrew Mao is right. Meteor now has Meteor.wrapAsync() for this kind of situation.

Here's the simplest way to do a charge via stripe and also pass a callback function:

var stripe = StripeAPI("key");    
Meteor.methods({

yourMethod: function(callArg) {

var charge = Meteor.wrapAsync(stripe.charges.create, stripe.charges);
charge({
amount: amount,
currency: "usd",
//I passed the stripe token in callArg
card: callArg.stripeToken,
}, function(err, charge) {
if (err && err.type === 'StripeCardError') {
// The card has been declined
throw new Meteor.Error("stripe-charge-error", err.message);
}

//Insert your 'on success' code here

});
}
});

I found this post really helpful:
Meteor: Proper use of Meteor.wrapAsync on server

Meteor.WrapAsync don't return value

You are wrapping the wrong function here, Meteor.wrapAsync transforms an async function (this means a function which transmits its result to the caller via a callback) in a synchronous one.

The function you pass to Meteor.wrapAsync does not have a callback as final argument, you should instead wrap _stripe.charge.create.

stripe.charge = function (stripeToken) {
var _stripe = StripeAPI(stripeToken);
var stripeChargeSync = Meteor.wrapAsync(_stripe.charge.create,_.stripe.charge);
var response = stripeChargeSync({
amount: 1000, // amount in cents, again
currency: "cad",
card: stripeToken.id,
description: "paid@whatever"
});
return response;
};

If you want to handle errors, you should use a try/catch block when calling stripe.charge.

try{
stripe.charge(STRIPE_TOKEN);
}
catch(exception){
console.log("Sorry we couldn't charge the money",exception);
}

I see you are logging your error using alert, are you trying to use Meteor.wrapAsync on the client ? Meteor.wrapAsync is meant to be used on the server because the environment needed to provide synchronous-looking execution is available in Node.js, not the browser.



Related Topics



Leave a reply



Submit