Meteor:在服务器上正确使用Meteor.wrapAsync

Meteor: Proper use of Meteor.wrapAsync on server

本文关键字:Meteor wrapAsync 服务器      更新时间:2023-09-26

背景

我正在尝试将条纹支付集成到我的网站中。我需要使用我的私有条纹密钥创建一个条纹用户。我将这个密钥存储在我的服务器上,并调用一个服务器方法来创建用户。也许还有其他方法可以做到这一点?下面是stripeapi(为了方便起见,复制到下面):https://stripe.com/docs/api/node#create_customer

//stripe api call
var Stripe = StripeAPI('my_secret_key');
Stripe.customers.create({
  description: 'Customer for test@example.com',
  card: "foobar" // obtained with Stripe.js
}, function(err, customer) {
  // asynchronously called
});

我的尝试和结果

我一直在使用相同的客户端代码和不同的服务器代码。所有尝试都会立即在客户端控制台上给出未定义的.log(…),但在服务器控制台上给出正确的响应。log(…):

//client
Meteor.call('stripeCreateUser', options, function(err, result) {
  console.log(err, result);
});
//server attempt 1
var Stripe = StripeAPI('my_secret_key');
Meteor.methods({
    stripeCreateUser: function(options) {  
        return Meteor.wrapAsync(Stripe.customers.create({
            description: 'Woot! A new customer!',
            card: options.ccToken,
            plan: options.pricingPlan
        }, function (err, res) {
            console.log(res, err);
            return (res || err);
        }));
    }
});
//server attempt 2
var Stripe = StripeAPI('my_secret_key');
Meteor.methods({
    stripeCreateUser: function(options) {  
        return Meteor.wrapAsync(Stripe.customers.create({
            description: 'Woot! A new customer!',
            card: options.ccToken,
            plan: options.pricingPlan
        }));
    }
});

我也在没有Meteor.wrapAsync.的情况下尝试过这两种方法

编辑-我也在使用这个软件包:https://atmospherejs.com/mrgalaxy/stripe

来自Meteor.wrapAsynchttp://docs.meteor.com/#meteor_wrapasync您可以看到,您需要向它传递一个函数和可选的上下文,而在您的两次尝试中,您将传递调用Stripe.customers.create的异步版本的RESULT。

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将异步函数转换为一个方便的同步查找函数,该函数允许按顺序编写代码。(在后台,所有内容仍然在异步Node.js事件循环中执行)。

我们需要将API函数(Stripe.customers.create)和函数上下文传递给Meteor.wrapAsync,即API函数体中的this,在本例中为Stripe.customers

编辑:

如何检索错误?

传统的节点式API函数通常将回调作为最后一个参数,该参数将在完成所需任务时最终被调用。这个回调有两个参数:error和data,根据调用结果,其中任何一个都将为null。

我们如何使用Meteor.wrapAsync返回的同步封装函数来访问错误对象?

我们必须依赖于使用try/catch块,因为在出现错误的情况下,它将由sync函数抛出,而不是作为异步函数回调的第一个参数传递。

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);
});

为什么Stripe不需要通过?

JavaScript没有"名称空间"的概念,因此API开发人员使用了定义一个全局对象作为API名称空间的常用技巧,该对象上定义的属性是API的"子模块"。这意味着Stripe.customers是Stripe API的子模块,用于公开与客户相关的函数,因此这些函数的this上下文是Stripe.customers,而不是Stripe

您可以通过在浏览器控制台中复制粘贴以下嘲讽代码来自己测试:

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

然后在浏览器控制台中调用存根函数,如下所示:

> Stripe.customers.create();
true

另一个选项是这个包,它可以实现类似的目标。

meteor add meteorhacks:async

从包自述:

异步包装(函数)

包装一个异步函数,并允许它在Meteor中运行,而不需要回调

//declare a simple async function
function delayedMessge(delay, message, callback) {
  setTimeout(function() {
    callback(null, message);
  }, delay);
}
//wrapping
var wrappedDelayedMessage = Async.wrap(delayedMessge);
//usage
Meteor.methods({
  'delayedEcho': function(message) {
    var response = wrappedDelayedMessage(500, message);
    return response;
  }
});

首先,感谢@saimeunt的回答,这让一些困难的概念变得清晰起来。然而,我遇到了一个问题,想要一个经典的异步回调(错误,结果)在客户端上显示错误和结果,这样我就可以在浏览器中提供信息。

我是这样解决的:

服务器代码:

var Stripe = StripeAPI(STRIPE_SECRET_KEY);
Meteor.methods({
    createCust: Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)
});

客户代码:

var stripeCallOptions = {
    description: 'Woot! A new customer!',
    card: ccToken,
    plan: pricingPlan
};

Meteor.call('createCust', stripeCallOptions, function(error, result){
    console.log('client error', error);
    console.log('client result', result);
});

看起来很整洁。然而不幸的是,wrapAsync有一个打开的错误(请参阅https://github.com/meteor/meteor/issues/2774)因为它没有将正确的错误恢复到调用者。一位名叫Faceyspacey的天才写了一个名为Meteor.makeAsync()的替代品,你会在我提到的错误页面上找到它,但它会将结果或错误返回到"result"变量,而"error"变量未定义。这对我来说还可以,至少我已经找到了正确的错误对象。

如果你使用makeAsync(),你将需要像这样导入Futures:

Meteor.startup(function () {
    //this is so that our makeAsync function works
    Future = Npm.require('fibers/future');
});

由于您几乎需要将每个函数封装在Async中,因此您应该使用此包https://atmospherejs.com/copleykj/stripe-sync,它使用WrapAsync预封装了所有条带函数,使您的生活更轻松,代码更干净。