Returning data multiple times using meteor method


#1

Is there any way to use return statement twice in meteor method?
Meteor.methods({
getData() {
let i = 0;
while(i <= 2) {
return 'check ’ + i
i++;
}
}
})

Currently, This method sends response only one time. Can we modify this method to send response twice? Is there any way to do this. (without calling the method multiple time)


#2

This seems like a strange request :smile: Why do you want this?


#3

In the method, I’m doing the 3rd party API calls multiple times to fetch the data according to the entity. and I just want to know that Is there any way to using return statement multiple times, to return the response when we get data from 1st API.


#4

You could address this several ways:

  1. Combine the many results into a single object and return that once.
  2. Make multiple calls to multiple methods.
  3. Use a generator, which lets you return multiple times.

Meteor method queue processing
#5

Use a generator, which lets you return multiple times.

I’m familiar with generator function. Can you please clarify the point with an example ?


#6

As you’re familiar with generators, you’ll appreciate any example I could give would be trivial. Just iterate over a generator from your method.


#7

For anybody else that finds this thread on Google:

I’ve put together an atmosphere package that allows using generator functions as Meteor methods, so you can return more than once:

dmihal:generator-method

I’d love to know if anybody’s got feedback on the package.


#8

Hi, and sorry for raising the dead…
I didn’t manage to turn meteor method into generator. can you help please?
Let’s say this is my generator:

*printlog() {
yield ‘wait!’;
return ‘end’;
}

and this is the function that calls it:

learn(e) {
e.preventDefault();
const getLearn = this.printlog();
console.log(getLearn.next().value);
console.log(getLearn.next().value);
}

when they’re both on client everything works. but when I tried to use the generator as a method
(eg "const getLearn = Meteor.call(‘printlog’)),
I got err msg “Cannot read property ‘next’ of undefined”. that also happened when I put the logs inside a callback of Meteor.call.
What am I missing?
Thanks in advance


#9
// server/main.js
import { Meteor } from 'meteor/meteor';

function* gen() {
  yield(1);
  yield(22);
  yield(333);
  return null;
}

Meteor.methods({
  getNext() {
    return gen().next().value;
  },
});
// client main.js
Meteor.call('getNext', (error, result) => {
  if (! error) console.log(result);
});


#10

Thanks for the quick reply.
It only logged “1”, and also on server side getNext ran only once, when I added log there it also logged only once.


#11

You need to call the method repeatedly. Each time it’s called you’ll get the next value.


#12

I called it 3 times, each time inside the “if” of previous call. it logged “1” 3 times.
and it makes sense, because each time we call the method, it calls the generator from fresh…


#13

Sorry, my bad. That’s what happens when you write code from memory in a meeting! I forgot to initialise the generator:

// server/main.js
import { Meteor } from 'meteor/meteor';

function* gen() {
  yield(1);
  yield(22);
  yield(333);
  return null;
}

const g = gen();

Meteor.methods({
  getNext() {
    return g.next().value;
  },
});

#14

Came in here to potentially suggest two of the three options that @robfallows did - did not even think to use generators (who knew they had a practical use case!!*).

Mind blown, cheers for the lesson!

  • Still failing to see why multiple returns would be useful in a practical setting

#15

Are you sure this will work without any side-effects?

What if different users call the method at the same time?

req 1 - this.userId === foo
req 2 - this.userId === bar
req 3 - this.userId === foo

Will every user have it’s own Meteor.methods instance? Or is it a shared global? I don’t think methods are bound to the request context / socket instance? Or are they?

Especially in the code sample you’re providing, as const g = gen(); is clearly on the global scope.


#16

That was never intended as a definitive solution, just a demonstration. The way to do it properly is to use the connection id inside the method as an index to an array of generators. A new connection instantiates a generator. On disconnect, the instance is destroyed, to prevent memory leaks.

I’m not at my computer, so can’t show the code atm.


#17

Thanks everyone!!

So to summarize:

  1. You can’t create the method itself as a generator and then initialize it from the client (which is a pity).
  2. you should define & initialize the generator in the server side, and call the initialized instance from the method, and then call the method from the client few times, as much as the number of yields in the generator.
  3. To avoid multy-users problems, you should establish a system to create a different instance for every use of the method, and manage it in array/dict.

Is that all? :slight_smile:


#18

If multiple users need to share potentially changing data, it sounds like you need to use a database!

Your method should be as simple as:

getNext() {
    Meteor.defer(() => {
       // Check if we're refreshing by updating a single "control" mongo document and see if anything changes
       const refreshing = ControlCollection.update({_id: 'control', refreshing: false}, {$set: {refreshing: true}}) === 1
       if (refreshing) { return; }
       // If we aren't, start fetching the latest data from whatever http stuff you want and insert it
       // into a database the client is subscribed to
       // We can now allow somewhere else to do the refresh
       ControlCollection.update({_id: 'control', refreshing: true}, {$set: {refreshing: false}})
    })
}

getNext() will return immediately. Update a timestamp in the collection to indicate the freshness of the data. Never return data in methods!


#19

Why is returning data from a method a bad thing?


#20

It is definitely okay to return data from methods. It’s very commonly used to avoid reactivity, or as a faster way to grab data from the server.

Ideally methods should be idempotent, with obvious exceptions for things like OP’s use case