The answer in - "What's going on with Meteor and Fibers/bindEnvironment()?" is very helpful however it could not help me resolve my issue.
Here is what I am doing :
- Login with google
- Call FunGoogle(user) from Accounts.onCreateUser
code:
SocialFunGoogle = function (user) {
var config = Accounts.loginServiceConfiguration.findOne({service: 'google'});
var opts = {
consumerKey: config.clientId,
consumerSecret: config.secret,
token: user.services.google.accessToken,
refreshToken: user.services.google.refreshToken
};
var gcontacts = new GoogleContacts(opts);
gcontacts.refreshAccessToken(opts.refreshToken, function (err, accessToken) {
if (err && err != null) {
console.log('gcontact.refreshToken, ', err);
return false;
} else {
console.log('gcontact.access token success!');
gcontacts.token = accessToken;
}
});
var fn = Meteor.bindEnvironment(function () {
var Fiber = Meteor.require('fibers');
var Future = Meteor.require('fibers/future');
var future = new Future();
setTimeout(function () {
return future.return(
Fiber(function () {
gcontacts.getContacts(
function (err, contact) {
contact = [{
name: 'S Sharma',
email: '[email protected]',
photoUrl: 'https://www.google.com/m8/feeds/photos/media/procrazium%40gmail.com/adf456aaaabbnndaa',
mime_type: 'image/*'
}, {
name: 'A Kapil',
email: '[email protected]',
photoUrl: 'https://www.google.com/m8/feeds/photos/media/procrazium%40gmail.com/22aaaab555758bc37952',
mime_type: 'image/*'
}, {
name: 'A Kartik',
email: '[email protected]',
photoUrl: 'https://www.google.com/m8/feeds/photos/media/procrazium%40gmail.com/2f2aaa02aab00f7aa85a2',
mime_type: 'image/*'
}];
contact.map(function (c) {
SocialConnect.insert(c);
});
return contact;
});
}).run()
);
}, 500);
});
fn();
}
When I try logging in, my code throws the following errors.
Exception while invoking method 'login' TypeError: Cannot set property '_meteor_dynamics' of undefined
Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment
Can you please point out what am I doing here wrong?
This isn't the correct way to do async in JavaScript:
SocialFunGoogle
reaches// A
before the code at// B
executes, so there's no guarantee thatgcontacts.token
has been set. (That's probably what the timeout is for, but that's not reliable since then the function will mysteriously fail if refreshing the token takes more than 500ms.)return
at// C
returns from the anonymous callback function, not fromSocialFunGoogle
, so this won't do what you want.Normally you would have to nest the rest of your function inside this callback, but Meteor uses Fibers, so you don't have to. Fibers lets you write code as if it were synchronous, but behind the scenes it still runs asynchronously like any other node.js application.
Unfortunately most npm packages are written for the callback style, but there are simple ways to adapt callback code for Fibers. I'll use Futures. Here's how you could rewrite that code using Futures:
Here's how that executes:
wait()
on the Future. The current Fiber goes to sleep, waiting for the Future to be resolved.return
method.future.return
pops out offuture.wait
.You can use this pattern to deal with the
getContacts
call as well. Since the only code that runs outside of the Fiber is thefuture.return
calls, you don't ever need tobindEnvironment
or create your own Fiber.