最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Returning value from callback within Meteor.method - Stack Overflow

programmeradmin1浏览0评论

I am running into something I don't understand with Meteor. I have this method, which takes a query, sends it to amazon, and then in the callback of that function I try to return the results.

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

But when I put the following into the console in my browser (chrome):

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

I get the this:

undefined
response: undefined          VM13464:3

I think I understand that the first undefined comes from the method not returning anything on the client, but the callback doesn't seem to work at all.

The amazon.execute(...) is definitely returning something, as the console.log right above the return does log the info I'm looking for.

Any ideas what's going wrong and how I can fix it?

I am running into something I don't understand with Meteor. I have this method, which takes a query, sends it to amazon, and then in the callback of that function I try to return the results.

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

But when I put the following into the console in my browser (chrome):

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

I get the this:

undefined
response: undefined          VM13464:3

I think I understand that the first undefined comes from the method not returning anything on the client, but the callback doesn't seem to work at all.

The amazon.execute(...) is definitely returning something, as the console.log right above the return does log the info I'm looking for.

Any ideas what's going wrong and how I can fix it?

Share Improve this question asked Nov 20, 2013 at 6:22 fnsjdnfksjdbfnsjdnfksjdb 1,6635 gold badges20 silver badges33 bronze badges
Add a comment  | 

4 Answers 4

Reset to default 18

You need to use Future to achieve your goal.

How to use future since Meteor 0.6?

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

 // use Future here
}

Your method rewritten with 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();
 }
});

Now it should work.

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

});

If you want to learn more about Future library I recommend watching screencast


Update on 26/12/2017

I just wanted to update this answer as you can achieve the same thing using promise and so, get rid of the "fibers" depedencies :)

An example is worth a thousand words

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

For anyone new to Meteor seeing this question and wondering why a library like Future or Fiber is necessary, it's because that call to amazon.execute is asynchronous.

In Javascript, many operations that take an extended period of time don't run one line after the next; Examples like writing to a database, using window.setTimeout, or making HTTP requests. With methods like these, historically you've needed to wrap the code you want to run after the fact in a callback.

Future and Fibers provide syntactic sugar and additional functionality, but their core functionality is the same.

Meteor uses special behind-the-scenes tricks to make certain built-in operations (like accessing MongoDB) appear synchronous, while still taking advantage of the increased performance of asynchronous code. For this reason, you normally only have to worry about async when using external packages (like the Amazon one in this example).


Here's a fully fleshed-out example of using both Future and Fibers:

There are some great articles explaining the nature of Sync/Async in Meteor on the Discover Meteor blog and at the Meteor Chef

Meteor methods are asynchronous, you can get the result by many way.

Using npm module fibers (The other answer are explaining it very clearly).

There are some other way w/o using npm module :

Via Session variable :

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

Or Via template variable :

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

Hope it will help !

one better solution

using Fiber package

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

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

        return Fiber.yield();
    }
});
发布评论

评论列表(0)

  1. 暂无评论