Метеор: вызов асинхронной функции внутри Meteor.method и возвращение результата

Я хочу вызвать асинхронную функцию внутри метода Meteor, а затем вернуть результат из этой функции в Meteor.call.

(Как) возможно?

Meteor.methods({
  my_function: function(arg1, arg2) {
    //Call other asynchronous function and return result or throw error
  }
});

Ответ 1

Эндрю Мао прав. Meteor теперь Meteor.wrapAsync() для такого рода ситуаций.

Здесь простейший способ выполнить заряд через полосу, а также передать функцию обратного вызова:

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

        });
    }
});

Я нашел это сообщение действительно полезным: Метеор: правильное использование Meteor.wrapAsync на сервере

Ответ 2

Используйте это будущее. Вот так:

Meteor.methods({
  my_function: function(arg1, arg2) {

    // Set up a future
    var fut = new Future();

    // This should work for any async method
    setTimeout(function() {

      // Return the results
      fut.ret(message + " (delayed for 3 seconds)");

    }, 3 * 1000);

    // Wait for async to finish before returning
    // the result
    return fut.wait();
  }
});

Обновление

Чтобы использовать Будущее, начиная с Meteor 0.5.1, вы должны запустить следующий код в методе Meteor.startup:

Meteor.startup(function () {
  var require = __meteor_bootstrap__.require
  Future = require('fibers/future');

  // use Future here
});

  Обновление 2:

Чтобы использовать Future начиная с Meteor 0.6, вам нужно запустить следующий код в методе Meteor.startup:

Meteor.startup(function () {
  Future = Npm.require('fibers/future');

  // use Future here
});

а затем используйте метод return вместо метода ret:

Meteor.methods({
  my_function: function(arg1, arg2) {

    // Set up a future
    var fut = new Future();

    // This should work for any async method
    setTimeout(function() {

      // Return the results
      fut['return'](message + " (delayed for 3 seconds)");

    }, 3 * 1000);

    // Wait for async to finish before returning
    // the result
    return fut.wait();
  }
});

См. этот метод.

Ответ 3

Последние версии Meteor предоставили недокументированную функцию Meteor._wrapAsync, которая превращает функцию со стандартным обратным вызовом (err, res) в синхронную функцию, что означает, что текущее волокно дает до возврата обратного вызова, а затем использует Meteor.bindEnvironment для обеспечения что вы сохраняете текущие переменные среды Метеор (например, Meteor.userId()).

Простым использованием будет следующее:

asyncFunc = function(arg1, arg2, callback) {
  // callback has the form function (err, res) {}

};

Meteor.methods({
  "callFunc": function() {
     syncFunc = Meteor._wrapAsync(asyncFunc);

     res = syncFunc("foo", "bar"); // Errors will be thrown     
  }
});

Вам также может потребоваться использовать function#bind, чтобы убедиться, что asyncFunc вызывается с правильным контекстом перед его переносом.

Для получения дополнительной информации см. https://www.eventedmind.com/tracks/feed-archive/meteor-meteor-wrapasync

Ответ 4

Другим вариантом является пакет, который достигает аналогичных целей.

meteor add meteorhacks:async

Из пакета README:

Async.wrap(функция)

Оберните асинхронную функцию и разрешите ее запускать внутри 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;
  }
});