Возвращаемое значение из обратного вызова в Meteor.method

Я сталкиваюсь с чем-то, что я не понимаю с Метеором. У меня есть этот метод, который принимает запрос, отправляет его в Amazon, а затем в обратном вызове этой функции я пытаюсь вернуть результаты.

Meteor.methods({
    'search': function(query) {
        var bookInfo;
        if (Meteor.isServer) {
            amazon.execute('ItemSearch', {
                'SearchIndex': 'Books',
                'Keywords': query,
                'ResponseGroup': 'ItemAttributes'
            }, function(results) {
                bookInfo = results;
                console.log(bookInfo);
                return bookInfo;
            });
        }
    }
});

Но когда я вставляю в консоль своего браузера (chrome) следующее:

Meteor.call('search', 'harry potter', function(error, response) {
    console.log('response:', response);
});

Я получаю это:

undefined
response: undefined          VM13464:3

Мне кажется, я понимаю, что первое неопределенное происходит от метода, который ничего не возвращает клиенту, но обратный вызов, похоже, не работает вообще.

Amazon.execute (...) определенно что-то возвращает, так как console.log прямо над return записывает искомую информацию.

Есть идеи, что происходит и как я могу это исправить?

4 ответа

Решение

Вам нужно использовать будущее, чтобы достичь своей цели.

Как использовать будущее, начиная с Meteor 0.6?

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

 // use Future here
}

Ваш метод переписан с Future:

Meteor.methods({
 'search': function(query) {

    var future = new Future();

    amazon.execute('ItemSearch', {
            'SearchIndex': 'Books',
            'Keywords': query,
            'ResponseGroup': 'ItemAttributes'
    }, function(results) {
       console.log(results);

       future["return"](results)

    });

    return future.wait();
 }
});

Теперь это должно работать.

Meteor.call('search', 'harry potter', function(error, response) {
   if(error){
    console.log('ERROR :', error);
   }else{
    console.log('response:', response);
   }

});

Если вы хотите узнать больше о библиотеке будущего, я рекомендую посмотреть скринкаст


Обновление от 26.12.2017

Я просто хотел обновить этот ответ, так как вы можете достичь того же, используя обещание и, таким образом, избавиться от зависимостей "волокон":)

Пример стоит тысячи слов

import scrap from 'scrap';

Meteor.methods({
    'hof.add'(el) {
        check(el, {
            _link: String
        });

        const promise = getHofInfo(el._link)
            .then((inserter) => {
                inserter.owner = Meteor.userId();
                Hof.insert(inserter);
                return true;
            })
            .catch((e) => {
                throw new Meteor.Error('500', e.message);
            });
        return promise.await();
    }
});


function getHofInfo(_link) {
    return new Promise((resolve, reject) => {
        scrap(_link, function (err, $) {
            if (err) {
                reject(err);
            } else {
                const attakers = $('#report-attackers').find('li').text();
                const defender = $('#report-defenders').find('li').text();
                const _name = attakers + ' vs ' + defender;
                const _date = new Date();
                resolve({ _name, _date, _link });
            }
        });
    });
}

Метеоритные методы асинхронны, вы можете получить результат разными способами.

Использование волокон модуля npm (другой ответ объясняет это очень четко).

Есть другой способ без использования модуля npm:

Через переменную сеанса:

    Meteor.call('myMethod',args, function(error, result) { 
  if (error) { Session.set('result', error) } // Note that the error is returned synchronously 
  else { 
    Session.set('result', result) // Using : Session.get('result') will return you the result of the meteor call !
  }
});

Или через переменную шаблона:

    Template.hello.onCreated(function helloOnCreated() {
  // counter starts at 0
  this.message = new ReactiveVar(0);
});

Template.hello.helpers({
  message() {
    return Template.instance().message.get();
  },
});

Template.hello.events({
  'click button'(event, instance) {
    Meteor.call('myMethod', args, function (error, result) {
      if (error) { Template.instance().message.set(error); }
      else {
        Template.instance().message.set(result);
      }
    })
  },
});

Надеюсь, это поможет!

Для любого новичка в Метеоре, который видит этот вопрос и задается вопросом, зачем нужна такая библиотека, как Future или Fiber, это потому, что этот вызов amazon.execute является асинхронным.

В Javascript многие операции, которые занимают длительный период времени, не запускают одну строку после следующей; Примеры, такие как запись в базу данных, использование window.setTimeout или выполнение HTTP-запросов. С помощью таких методов исторически вам нужно было обернуть код, который вы хотите запустить после факта, в обратный вызов.

Future и Fibers предоставляют синтаксический сахар и дополнительную функциональность, но их основная функциональность одинакова.

Meteor использует специальные закулисные приемы, чтобы определенные встроенные операции (например, доступ к MongoDB) выглядели синхронными, но при этом все еще использовали преимущества повышенной производительности асинхронного кода. По этой причине вам обычно приходится беспокоиться только об асинхронности при использовании внешних пакетов (например, Amazon в этом примере).


Вот подробный пример использования Future и Fibers:

Есть несколько замечательных статей, объясняющих природу Синхронизации / Асинхронизации в Метеоре, в блоге Discover Meteor и в Meteor Chef.

одно лучшее решение

используя пакет Fiber

var Fiber = Npm.require('fibers');
...
Meteor.methods({
    callAsync: function (args) {
        var fiber = Fiber.current;

        async(function (args) {
            ...
            fiber.run(res);
        });

        return Fiber.yield();
    }
});
Другие вопросы по тегам