Javascript Meteor:在服务器上正确使用 Meteor.wrapAsync
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26226583/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Meteor: Proper use of Meteor.wrapAsync on server
提问by Adam
Background
背景
I'm trying to integrate stripe payments into my site. I need to create a stripe user using my private stripe key. I'm storing this key on my server, and calling a server method to create the user. Maybe there's another way of accomplishing this? Here's the stripe api (copied below for convenience): https://stripe.com/docs/api/node#create_customer
我正在尝试将条带支付集成到我的网站中。我需要使用我的私有条带密钥创建一个条带用户。我将此密钥存储在我的服务器上,并调用服务器方法来创建用户。也许还有另一种方法可以实现这一目标?这是条带 api(为方便起见复制如下):https: //stripe.com/docs/api/node#create_customer
//stripe api call
var Stripe = StripeAPI('my_secret_key');
Stripe.customers.create({
description: 'Customer for [email protected]',
card: "foobar" // obtained with Stripe.js
}, function(err, customer) {
// asynchronously called
});
My attempts and results
我的尝试和结果
I've been using the same client code with different server code. All attempts immediately give undefined on the client's console.log(...) but give the proper response on the server console.log(...):
我一直在使用具有不同服务器代码的相同客户端代码。所有尝试立即在客户端的 console.log(...) 上给出 undefined 但在服务器 console.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
}));
}
});
I've also tried both without Meteor.wrapAsync.
我也试过没有 Meteor.wrapAsync。
EDIT - I'm also using this package: https://atmospherejs.com/mrgalaxy/stripe
编辑 - 我也在使用这个包:https: //atmospherejs.com/mrgalaxy/stripe
回答by saimeunt
From the Meteor.wrapAsynchttp://docs.meteor.com/#meteor_wrapasyncyou 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.wrapAsynchttp://docs.meteor.com/#meteor_wrapasync您可以看到您需要向它传递一个函数和一个可选的上下文,而在您的两次尝试中,您正在传递调用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.wrapAsynctransforms 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).
Meteor.wrapAsync将异步函数转换为方便的同步查找函数,允许编写顺序查找代码。(底层的一切仍然在异步 Node.js 事件循环中执行)。
We need to pass to Meteor.wrapAsyncour API function (Stripe.customers.create) along with the function context, ie thisinside the body of the API func, which in this case is Stripe.customers.
我们需要传递给Meteor.wrapAsync我们的 API 函数 ( Stripe.customers.create) 以及函数上下文,即this在 API func 的主体内部,在这种情况下是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.
传统的节点样式 API 函数通常将回调作为最后一个参数,当所需的任务完成时,该回调将最终被调用。这个回调有 2 个参数:error 和 data,根据调用的结果,其中一个将为 null。
How do we access the error object using the synchronous wrapped functions returned by Meteor.wrapAsync?
我们如何使用返回的同步包装函数访问错误对象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/catch 块,因为如果出现错误,它将被同步函数抛出,而不是作为异步函数回调的第一个参数传递。
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?
为什么不需要通过 Stripe?
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.customersis a submodule of the Stripe API exposing customers-related funcs, and as such these funcs thiscontext is Stripe.customers, not Stripe.
JavaScript 没有“命名空间”的概念,因此 API 开发人员使用定义一个全局对象作为 API 命名空间的常用技巧,在该对象上定义的属性是 API 的“子模块”。这意味着它Stripe.customers是 Stripe API 的一个子模块,用于公开与客户相关的 funcs,因此这些 funcsthis上下文是Stripe.customers,不是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
回答by FullStack
Another option is this packagewhich achieves the similar goals.
另一种选择是这个包,它实现了类似的目标。
meteor add meteorhacks:async
From the package README:
从包自述文件:
Async.wrap(function)
异步包装(函数)
Wrap an asynchronous function and allow it to be run inside Meteor without callbacks.
包装一个异步函数并允许它在没有回调的情况下在 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;
}
});
回答by mwarren
First of all, thanks to @saimeunt for his answer, which makes some difficult concepts clear. However I had the problem of wanting a classic async callback(err, result) showing both the error and the result back on the client, so that I can give informative messages in the browser.
首先感谢@saimeunt 的回答,让一些难懂的概念变得清晰。但是,我想要一个经典的异步回调(错误,结果)在客户端上显示错误和结果,以便我可以在浏览器中提供信息性消息。
I solved it this way:
我是这样解决的:
Server code:
服务器代码:
var Stripe = StripeAPI(STRIPE_SECRET_KEY);
Meteor.methods({
createCust: Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)
});
Client code:
客户端代码:
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);
});
Looks neat. However unfortunately wrapAsync has an open bug, (see https://github.com/meteor/meteor/issues/2774) because it doesn't restore the proper error to the caller. A genius called Faceyspacey has written a replacement called Meteor.makeAsync() which you will find on the bug page I mentioned, which however returns either the result OR the error to the 'result' variable, leaving the 'error' variable undefined. That's fine by me for now, at least I've got a hook on the proper error object.
看起来很整洁。然而不幸的是 wrapAsync 有一个开放的错误,(参见https://github.com/meteor/meteor/issues/2774),因为它没有将正确的错误恢复给调用者。一位名叫 Faceyspacey 的天才编写了一个名为 Meteor.makeAsync() 的替代品,您可以在我提到的错误页面上找到它,但是它会将结果或错误返回给 'result' 变量,而未定义 'error' 变量。我现在很好,至少我在正确的错误对象上有一个钩子。
If you use makeAsync() you will need to import Futures like this:
如果你使用 makeAsync() 你需要像这样导入 Futures:
Meteor.startup(function () {
//this is so that our makeAsync function works
Future = Npm.require('fibers/future');
});
回答by Patrick Mencias-lewis
Since you are going to need almost every function to be wraped in Async what you should do is use this package https://atmospherejs.com/copleykj/stripe-syncit prewraps all stripe functions with WrapAsync making your life easier and code cleaner.
由于您几乎需要将所有函数都包装在 Async 中,因此您应该做的是使用这个包,https://atmospherejs.com/copleykj/stripe-sync它使用WrapAsync 预先包装所有条带函数,使您的生活更轻松,代码更简洁。

