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 :

  1. Login with google
  2. 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.

  1. Exception while invoking method 'login' TypeError: Cannot set property '_meteor_dynamics' of undefined

  2. 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?

1

There are 1 best solutions below

0
On
gcontacts.refreshAccessToken(opts.refreshToken, function (err, accessToken) {
  // B
  if (err && err != null) {
    console.log('gcontact.refreshToken, ', err);
    return false; // C
  } else {
    console.log('gcontact.access token success!');
    gcontacts.token = accessToken;
  }
});
// A

This isn't the correct way to do async in JavaScript:

  • The SocialFunGoogle reaches // A before the code at // B executes, so there's no guarantee that gcontacts.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.)
  • The return at // C returns from the anonymous callback function, not from SocialFunGoogle, 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:

var future = new Future(); // 1

gcontacts.refreshAccessToken(opts.refreshToken, function (err, accessToken) { //2
  future["return"]({err: err, accessToken: accessToken}); // 4
});

var result = future.wait(); // 3, 5
if (result.err && result.err != null) { // 6
  console.log('gcontact.refreshToken, ', result.err);
  return false;
} else {
  console.log('gcontact.access token success!');
  gcontacts.token = result.accessToken;
}

Here's how that executes:

  1. We create a new, non-resolved Future.
  2. We fire off an asynchronous action.
  3. While the asynchronous action is still happening, we wait() on the Future. The current Fiber goes to sleep, waiting for the Future to be resolved.
  4. The asynchronous action completes. In the callback for that async action, we resolve the Future using its return method.
  5. Since the Future is resolved, the Fiber wakes up, and that object we passed into future.return pops out of future.wait.
  6. The function carries on as usual.

You can use this pattern to deal with the getContacts call as well. Since the only code that runs outside of the Fiber is the future.return calls, you don't ever need to bindEnvironment or create your own Fiber.