Meteor fibers "sync" mode is driving me crazy. Here is a simple code example :

var feedsData = feeds.fetch(); // [{_id: "1234"}, {_id: "6789", url: "http://...."}]
for(var i = 0, len = feedsData.length; i < len; i++) {
    var feed = feedsData[i];
    parser.parseURL(feed.url, function(err, out){
        console.log(feed._id, i); // outputs "6789" and "2" each times
    });
}

I don't understand how to make this work. The callback is called after the loop is over, but the internal internal variables such as feed should be preserved... and they are not.

The url parsed are good (the first one, then the second one), but then i can't update my data since I don't have the good _id in the callback.

The wanted output would be: "1234" "0" and "6789" "1", not "6789" "2" both times... How would you make this in Meteor / Fiber code ?

有帮助吗?

解决方案

Another way to do it in the "fiber"(and it is probably better than the answer with "future" I posted above) :

var feedsData = feeds.fetch(); // [{_id: "1234"}, {_id: "6789", url: "http://...."}]
Fiber(function() {
    var fiber = Fiber.current;
    for(var i = 0, len = feedsData.length; i < len; i++) {
        var feed = feedsData[i];
        parser.parseURL(feed.url, function(err, out) {
            console.log(feed._id, i);
            if(err) return fiber.throwInto(err);
            fiber.run();
        });
        Fiber.yield();
        console.log('here', i);
    }
    console.log('there');
}).run();
console.log('and there');

The output will be :

"and there"
"1234" "0"
"here" "0"
"6789" "1"
"here" "1"
"there"

Note that everything in the Fiber function is executed in its own fiber as if it was asynchrone, which is why "and there" is outputed first

其他提示

Not sure this has anything to do with Meteor, Fibers or "sync mode". I think it's just a bug in your javascript. You're looping through an array and then invoking a property of an object within a callback. Of course when the callback eventually is called, it will look at the current value of feed, which will be the most recently assigned one after the loop exited.

So you should rewrite your code to take that into account:

var feedsData = [{_id: "1234"}, {_id: "6789", url: "http://...."}]
for(var i = 0, len = feedsData.length; i < len; i++) {
    var feed = feedsData[i];
    parser.parseURL(feed.url, function(err, out){
        console.log(this._id, arguments[0]); // will output "1234 0" and "6789 1"
    }.bind(feed, i));
}

The simplest solution is:

feeds.fetch().forEach(function(feed,i) {
    parser.parseURL(feed.url, function(err, out){
        console.log(feed._id, i);
    });
});

Javascript does not have block scoping (yet, let is coming in ES6), only function scoping.

Ok, here is the "fiber" way of doing that :

var Future = require('fibers/future'),
wait = Future.wait,
feedsData = feeds.fetch(); // [{_id: "1234"}, {_id: "6789", url: "http://...."}],
parseUrl = Future.wrap(parser.parseURL);
Fiber(function() {
    for(var i = 0, len = feedsData.length; i < len; i++) {
        var feed = feedsData[i];
        var out = parseUrl(feed.url).wait();
        console.log('here', i, out);
    }
    console.log('there');
}).run();
console.log('and there');

The result output will be :

"and there"
"here" "0" "the out data from the 1st callback"
"here" "1" "the out data from the 2nd callback"
"there"

Just what you would expect. The "future" in Fibers expects that the last parameter given to the function is a callback, and will return err as the first parameter

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top