Callback to Meteor.call( ) not working

Hey guys!

I have a Meteor back-end connected to a Chrome Extension through a DDP client.
On Meteor, I defined a method; on the Extension, I am calling it.
The call itself works, but the callback is not executed. I read through different posts discussing callback errors but couldn’t find a solution to this on any of them.

Meteor

/imports/api/methods.js

Meteor.methods({
    
    'testMethod': function(){
        console.log('Test');
        return 'Testing';
    }

});

Chrome Extension

/chrextension/src/popup.js

(function($){
        $('#testButton').click(function(){
            ddp.call('testMethod', function(error,result){
                console.log('I am here');
                console.log(result);
                $("h1").text(result);
            });
        });   
})(jQuery);

The console.log on the Method side is executed; those on the Call side are not. The $().text() doesn’t output anything, too.

Any ideas what could be wrong?

Thanks!

Did you try in the dev console to see what happens inside the client-side callback?

Since it’s a chrome extension, I can only access the internal console at chrome://extensions.
Nothing happens there (no logs, no errors).

We have developed a Chrome app (a bit different than extension) and are able to debug the JS. I don’t have experience with extensions so can’t really speak.

Which DDP lib are you using.

meteor-ddp.js: https://github.com/ef2k/meteor-ddp

My code is based on this boilerplate:

I have tried Asteroid, but since it works through “require”, the chrome extension won’t accept it.

The docs of meteor-ddp specifies that the call method does not accept a callback, instead it returns a jQuery deferred style promise:

call(methodName, [params, …]) - Does a Remote Procedure Call on any method exposed through Meteor.methods on the server. Returns -> Promise which resolves with any returned data.

And their example usage:

var createPlayer = ddp.call('createPlayer');
createPlayer.done(function(playerId) {
  var joinGame = ddp.call('joinGame', [playerId]);
  joinGame.done(function(gameId) {
    console.log("We joined a game, here's the game id: ", gameId);
  });
});

So your extension code would look like this (assuming you’ve already connected to the server):

(function($){
        $('#testButton').click(function(){
            var call = ddp.call('testMethod');
            call.done(function(result) {
                console.log('I am here');
                console.log(result);
                $("h1").text(result);
            });
        });   
})(jQuery);
1 Like

Thanks @coagmano, it works perfectly!

If I could ask for your help on a second related issue… I changed my DDP Client from meteor-ddp.js to simpleDDP due to other issues. There, callbacks to calls are based on promises, as explained here: https://github.com/Gregivy/simpleddp/blob/master/docs/api.md#simpleddpcallmethodarguments

However, I am having trouble with it - my results are being returned as undefined. Here is my code:

meteor/imports/api/methods.js

Meteor.methods({

     'runScrape': function(currentUrl){
            console.log(currentUrl);
            opengraph.getSiteInfo(currentUrl) //this is just an API that scrapes the current website
                .then(function(result){
                    const textTitle = result.hybridGraph.title; //this extracts the title of the website
                    console.log(textTitle);
                    return textTitle;
                })
                .catch(function(error){
                    console.log('Error');
                });
        }
})

chrome-extension/index.js

server.call('runScrape',[currentTabUrl])
            .then(function(result){
                $("#url").text('Test');
                $("#result").text(result);
            })
            .catch(function(error){
                $("#error").text("Error");
            });

The result for this is that the method works (the console.log on Meteor prints the correct textTitle) and the call works correctly for the first action (printing the string ‘Test’), but doesn’t print the result variable.
I ran a few tests and discovered that result is undefined. I checked the type of result on the server side, and it is a string.

Why is this happening, despite using promises to cope with the asyncronous behaviour of the call? Any idea?

Your return textTitle is inside the context of that specific then in the Promise chain. It’s not returning from the method, but passing the result on to the next step of the chain.

Quite honestly, you’ll find it so much easier to work with Promises if you use async / await syntax:

Meteor.methods({

  async runScrape(currentUrl) {
    try {
      console.log(currentUrl);
      const result = await opengraph.getSiteInfo(currentUrl); //this is just an API that scrapes the current website
      const textTitle = result.hybridGraph.title; //this extracts the title of the website
      console.log(textTitle);
      return textTitle;
    } catch (error) {
      console.log('Error');
    }
  }
});

You can refactor your client code similarly (just remember the nearest outer function needs to be declared async).

1 Like

Awesome! Thank you very much for the lesson :slight_smile:

1 Like

@robfallows It seems like opengraph does not have support for async/await… only callbacks and promises. Any thoughts on how I could refactor that code using promises? Thanks!

await waits on Promises. If you have a Promise, you can use async / await.

I changed the method to the async/await you wrote:

Meteor.methods({

  async runScrape(currentUrl) {
    try {
      console.log(currentUrl);
      const result = await opengraph.getSiteInfo(currentUrl); //this is just an API that scrapes the current website
      const textTitle = result.hybridGraph.title; //this extracts the title of the website
      console.log(textTitle);
      return textTitle;
    } catch (error) {
      console.log('Error');
    }
  }
});

And refactored my client code to:

$("#testButton").click(function(){
        async function runScrape(){
            try {
                const result = await server.call('runScrape',[currentTabUrl]);
                $("#url").text(result);
              } catch (error) {
                $("#error").text("Error");
              }

            }
        });

But nothing happens…
Before I refactored the client code (while the method was already async/await), the method itself worked, but with no output on the client side. It looks like it is still returning undefined.

It’s your click that needs to be async.

.click(async function() {

And lose the artificial function you added after.

Tried both below, but still doesn’t work (sorry for all the trouble I’m giving to you with this…):

$("#testButton").click(async function(){
            
        try {
                const result = await server.call('runScrape',[currentTabUrl]);
                $("#url").text(result);
              } catch (error) {
                $("#error").text("Error");
              }

    });
$("#testButton").click(async function(){
        
        const result = await server.call('runScrape',[currentTabUrl]);
        $("#url").text(result);
        
    });

You should give more details, do you see any error in your browser console? Or maybe in meteor console?
Also try this

$("#testButton").click(async function(){
        
        const result = await server.call('runScrape',[currentTabUrl]).catch(function(e) {console.log(e)});
        console.log('my result',result);
        $("#url").text(result);
        
    });
1 Like

That’s a real anti-pattern when using async / await. I’m not actually sure what you’d end up with as the result. The correct syntax is as @patrickcneuhaus used.

1 Like

I’ve tested your code as far as I can and it works correctly for me.

I have not tested your opengraph call: I don’t know which package you used, and I don’t have a FB account (if that matters). However, I tested against a different call which returns an asynchronous Promise and everything worked as expected.

1 Like

It finally worked!

$("#testButton").click(async function(){
        
        const result = await server.call('runScrape',[currentTabUrl]).catch(function(e) {});
        $("#result").text(result);
        
    });

Thank you very much for all the help, @robfallows and @gregivy

Just for the record:
I am using the opengraph-io package (NPM installed).

1 Like