web-dev-qa-db-ja.com

Meteor:サーバーでのMeteor.wrapAsyncの適切な使用

背景

ストライプ支払いを自分のサイトに統合しようとしています。プライベートストライプキーを使用してストライプユーザーを作成する必要があります。このキーをサーバーに保存し、サーバーメソッドを呼び出してユーザーを作成しています。たぶんこれを達成する別の方法がありますか?これがストライプ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
});

私の試みと結果

同じクライアントコードと異なるサーバーコードを使用しています。すべての試行は、クライアントのconsole.log(...)で未定義になりますが、サーバーの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
        }));
    }
});

Meteor.wrapAsyncを使用せずに両方を試しました。

編集-私もこのパッケージを使用しています: https://atmospherejs.com/mrgalaxy/stripe

50
Adam

Meteor.wrapAsyncから http://docs.meteor.com/#meteor_wrapasync 関数とオプションでコンテキストを渡す必要があることがわかりますが、2回の試行では呼び出しの結果を渡しています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は、非同期関数を、順番に見えるコードを書くことができる便利な同期的な関数に変換します。 (内部のすべてが非同期Node.jsイベントループ内で実行されます)。

API関数(Meteor.wrapAsync)と共に関数コンテキスト、つまりAPI funcの本体内のthis(この場合はStripe.customers.create)と共にStripe.customersに渡す必要があります。

編集:

エラーを取得する方法

従来のノードスタイルのAPI関数は、通常、最後の引数としてコールバックを受け取り、必要なタスクが完了すると最終的に呼び出されます。このコールバックは、エラーとデータの2つの引数を取ります。呼び出しの結果に応じて、どちらか1つがnullになります。

Meteor.wrapAsyncによって返される同期ラップ関数を使用して、エラーオブジェクトにアクセスするにはどうすればよいですか?

エラーの場合、非同期関数コールバックの最初の引数として渡されるのではなく、同期関数によってスローされるため、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);
});

stripeを渡す必要がないのはなぜですか?

JavaScriptには「名前空間」の概念がないため、API開発者は、API名前空間として機能するグローバルオブジェクトを定義する一般的なトリックを使用します。このオブジェクトで定義されるプロパティは、APIの「サブモジュール」です。これは、Stripe.customersが顧客関連のファンクを公開するStripe APIのサブモジュールであり、これらのファンクthisコンテキストがStripeではなくStripe.customersであることを意味します。

ブラウザコンソールでこのモックコードをコピーして、自分でテストできます。

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

そして、次のようにブラウザコンソールでスタブ関数を呼び出します。

> Stripe.customers.create();
true
74
saimeunt

別のオプションはこれです package これは同様の目標を達成します。

meteor add meteorhacks:async

パッケージのREADMEから:

Async.wrap(function)

非同期関数をラップし、コールバックなしで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;
  }
});
11
FullStack

まず、@ saimeuntの回答に感謝します。これにより、いくつかの難しい概念が明確になります。しかし、エラーと結果の両方をクライアントに表示する古典的な非同期コールバック(err、結果)が必要な問題があったため、ブラウザで情報メッセージを提供できます。

私はこのように解決しました:

サーバーコード:

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()と呼ばれる置換を書きました。これは私が言及したバグページにありますが、結果ORエラーを 'result'変数に返し、 'error'変数を未定義のままにしておきます。今のところは、これで問題ありません。少なくとも、適切なエラーオブジェクトにフックできました。

MakeAsync()を使用する場合、次のようにFutureをインポートする必要があります。

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

ほぼすべての関数をAsyncでラップする必要があるため、このパッケージを使用する必要がありますhttps://atmospherejs.com/copleykj/stripe-sync WrapAsyncを使用してすべてのストライプ関数を事前にラップし、作業を簡単にし、コードを簡潔にします。