jagi / meteor-astronomy

Model layer for Meteor
https://atmospherejs.com/jagi/astronomy
MIT License
605 stars 66 forks source link

Error: Can't call yield in a noYieldsAllowed block! #679

Closed dnish closed 6 years ago

dnish commented 6 years ago

Hey, I'm trying to add a value from another collection to one of my fields. This is the event I've defined:

events: {

        afterInit(e) {
            const doc = e.target;
            doc._logoUrl = Logos.findOne({_id:doc.logoId}).url;
        }

    }

The field is defined as transient:

_logoUrl: {
            type: String,
            transient: true,
        }

When returning the object by a method I get the following error:

Error: Can't call yield in a noYieldsAllowed block!

I've copied that kind of definition from another project (which runs on Meteor 1.5.2.2 and Astronomy 2.5.2) where it is working fine. Any idea whats wrong here?

lukejagodzinski commented 6 years ago

I've copied that kind of definition from another project where it is working fine

so just by saying that you've found source of your problem. It's not a problem with these lines of code but with some other part of your code. It's hard to tell what's causing this problem without minimal reproduction repository.

dnish commented 6 years ago

@lukejagodzinski Yeah, I've updated my old repo:

https://github.com/dnish/meteor-astronomy-code-share

The error occurs when we return an Astronomy object via method. When you find an object on the server and do not return it by method, everything is working fine. So this works without problems:

/server/test.js

const video = Video.findOne();
console.log(video); // Logs a video with modified fields.
lukejagodzinski commented 6 years ago

I will take a look at it tomorrow

dnish commented 6 years ago

@lukejagodzinski I've also tried version 2.5.2 which works on a older Meteor version, but still get the same error.

dnish commented 6 years ago

@lukejagodzinski Any updates on this? Sorry for the inquire, but this problem breaks our new app and I can't add anything new.

lukejagodzinski commented 6 years ago

@dnish you haven't updated repository

dnish commented 6 years ago

@lukejagodzinski Upps, sorry, just forgot to push it. Just pushed the changes now.

lukejagodzinski commented 6 years ago

Hmmm strange. Apparently they've changed something about how objects are passed over the network. It will take more time to investigate.

What you can do in meanwhile is returning raw object from the method:

return video.raw();

Or stringify and parse it manually:

// server
import { EJSON } from "meteor/ejson";
Meteor.methods({
  video() {
    const video = Video.findOne();
    return EJSON.stringify(video);
  }
});
// client
import { EJSON } from "meteor/ejson";
Meteor.call("video", function(err, res) {
  const video = EJSON.parse(res);
});
lukejagodzinski commented 6 years ago

@dnish ok I've found error in your code. You can do this:

events: {
  afterInit(e) {
    const doc = e.target;
    doc.comments = Comment.findOne();
  }
}

When you send instance of the Video class over network, then Meteor tries to recreate document on the client. Document first, gets stringified on the server, send over the network and parsed on the client. And it will also run all the Astronomy stuff like the afterInit event. The entire idea of fetching comment here is wrong. So you have to rethink your application logic.

dnish commented 6 years ago

@lukejagodzinski Well, it worked before on older Meteor versions. The comment example may be not the best, but in this case it is very helpful:

    afterInit(e) {
            const doc = e.target;
            doc._logoUrl = SitesLogos.findOne(doc.logoId).link();
        },

While SitesLogos is a MeteorFiles collection. Otherwise I would have to add the _url manually on every request which would be a Model anti pattern.

// Edit: Okay, there we've also used the raw() trick.