In my Meteor client code, I'm trying to use a third party API that only has asynchronous calls. How can I use Meteor.wrapAsync on the client to make calls to this API in a synchronous style? The docs seem to indicate that this is possible: http://docs.meteor.com/#/full/meteor_wrapasync
Here is some sample code I would like to call in a synchronous style:
var func1 = function(callback) {
Meteor.setTimeout(function() {
console.log('func1 executing');
callback(null, {success: true});
}, 2000);
};
var func2 = function(callback) {
Meteor.setTimeout(function() {
console.log('func2 executing');
callback(null, {success: true});
}, 1000);
};
var wrapped1 = Meteor.wrapAsync(func1);
var wrapped2 = Meteor.wrapAsync(func2);
Template.test.rendered = function() {
wrapped1();
console.log('After wrapped1()');
wrapped2();
console.log('After wrapped2()');
};
Currently, this produces this output:
After wrapped1()
After wrapped2()
func2 executing
func1 executing
I'd like it to produce:
func1 executing
After wrapped1()
func2 executing
After wrapped2()
I've put this code into a MeteorPad here: http://meteorpad.com/pad/fLn9DXHf7XAACd9gq/Leaderboard
Meteor.wrapAsync works on the client for the sake of isomorphic code. This is so you can make code that you can share on the client and server without Meteor crashing or complaining.
It is not possible to have synchronous code on the client. At least without using ES6 features which are not available on all browsers.
As in the comment by saeimeunt Meteor.wrapAsync will require a callback on the client.
Related
My client is making a call to the server.
Meteor.call('someRequest', params, onAllDoneCallback);
that is processed by (server code)
Meteor.methods({
'someRequest': function(params, cb) {
anAsyncFunction(params, cb);
return true;
},
});
I'd like the onAllDoneCallback to be triggered on the client side once the anAsyncFunction has finished and triggers its own callback.
However, in Meteor it seems that the second argument of someRequest is ignored and that the onAllDoneCallback is triggered with what someRequest returns, which here is true and which is called before that anAsyncFunction has finished.
In my case I'm more concerned about the timing issue (I'm using it to tell the client that the processing is finished, and not just that the request is well received) but other will probably want to call the callback with arguments from anAsyncFunction
What you are doing now is passing a function to the server. If that does work, it's very insecure. What you want to do is create a future and then use it to manage the asynchronous function. Here is an example:
let Future = Npm.require('fibers/future');
Meteor.methods({
someRequest: function (someArg) {
// for security reasons, make sure you check the type of arguments.
check(someArg, String);
// future is an async handler.
let future = new Future();
// this is a function for a generic node style callback [ie, function (err, res)]
let resolver = future.resolver();
// run your function and pass it the future resolver
// the future will be resolved when the callback happens.
anAsyncFunction(someArg, resolver);
// this meteor method will not end until future has been resolved.
return future.wait();
}
});
Alternatively, Meteor provides a wrapAsync that provides similar functionality of wrapping async functions in futures so they can run in meteor methods. That is:
let wrappedAsyncFunction = Meteor.wrapAsync(anAsyncFunction /** second argument is `this` binding*/);
return wrappedAsyncFunction();
adapting this answer: Meteor: Proper use of Meteor.wrapAsync on server
You have to use Meteor's wrapAsync api, which takes a function that accepts a callback as its last argument, the callback being like function(error, result){}. So it will look like:
Meteor.methods({
'someRequest': function(params){
var wrap = Meteor.wrapAsync(anAsyncFunction);
return wrap(params);
},
});
I am trying to consume a REST API in the meteor application. Inside the server.js file which is in the server folder, I have written this code:
Meteor.methods({
checkTwitter: function () {
this.unblock();
return Meteor.http.call("GET", "http://search.twitter.com/search.json?q=perkytweets");
}
});
Inside the client.js file, which is in the client folder, I have written down this code:
Meteor.call("checkTwitter", function(error, results) {
console.log(results.content); //results.data should be a JSON object
});
I get this error message in console:
"Exception while simulating the effect of invoking 'checkTwitter' Error: Can't make a blocking HTTP call from the client; callback required".
I have the callback function defined in client due to which I don't understand this error. What is it that I am doing wrong?
Meteor.http has been deprecated, please see the HTTP package.
I think that since there's a stub, "checkTwitter" will actually also run on the client. Once the server returns, its result will overwrite the result from teh client run.
In this case, since Meteor.http.call can't run on the client without a callback, you get the error.
Try changing:
Meteor.methods({
checkTwitter: function () {
this.unblock();
return Meteor.http.call("GET", "http://search.twitter.com/search.json?q=perkytweets");
}
});
With
Meteor.methods({
checkTwitter: function () {
if (Meteor.isServer) {
this.unblock();
return Meteor.http.call("GET", "http://search.twitter.com/search.json?q=perkytweets");
}
}
});
I'm using the Firebase node module and trying to convert it's callbacks to thunks to be able to use them in Koa.
This is the original event listener callback as per the Firebase documentation:
projects.on('value', function (snapshot) {
console.log('The read succeeded: ' + snapshot.val());
}, function (errorObject) {
console.log('The read failed: ' + errorObject.code);
});
And this is the where I want to add it in my Koa project:
function *list() {
// Get the data here and set it to the projects var
this.body = yield render('list', { projects: projects });
}
Anyone know how to to do it? Have tried thunkify, thunker and thu without success...
I don't think you can use thunkify etc because they are trying to convert a standard node function to a thunk. The firebase api doesn't follow the standard node.js callback signature of
fn(param1, parm2,.., function(err, result){});
which thunkify is expecting.
I think this would do it
var findProjectsByValue = function(value){
return function(callback){
projects.on(value, function(result){
callback(null, result);
}, function(err){
callback(err);
})
}
};
then you would consume it
var projects = yield findProjectsByValue('value');
Or you could just do rest api calls, which I assume is what you want. The firebase api seems to be more for evented scenarios, socketio etc
This has been bothering me for a while so I thought I'd just do a quick QA on it:
If one has a normal nodeJS module or something and it has a async function on the server side. How do I make it synchronous. E.g how would I convert the nodejs fs.stat asynchronous function to a synchronous one.
e.g I have
server side js
Meteor.methods({
getStat:function() {
fs.stat('/tmp/hello', function (err, result) {
if (err) throw err;
console.log(result)
});
}
});
If I call it from the client I get back undefined as my result because the result is in a callback.
There is a function (undocumented) called Meteor.wrapAsync.
Simply wrap the function up
Meteor.methods({
getStat:function() {
var getStat = Meteor._wrapAsync(fs.stat);
return getStat('/tmp/hello');
}
});
Now you will get the result of this in the result of your Meteor.call. You can convert any async function that has a callback where the first parameter is an error and the second the result.
I have problem with trying to turn asynchronous function into synchronous.
here is a method from class:
doPost: function(call, data) {
var uri = 'http://localhost/api/'+call;
var api = http.createClient(80, 'localhost');
var domain = 'localhost';
var request = api.request("POST", uri,
{'host' : domain,
'Content-Type' : 'application/x-www-form-urlencoded',
"User-Agent": this.userAgent,
'Content-Length' : data.length
});
request.write(data);
request.end();
request.on('response', function (response) {
response.on ('data', function (chunk) {
sys.puts(chunk);
try {
var result = JSON.parse(chunk);
//------------ the problem
return HOW_TO_RETURN_RESULT;
//------------ /the problem
}catch (err) {
return {'ok': 0, 'err': err}
}
});
});
},
Want to use this function in this way:
result = obj.doPost('getSomeData.php', '&data1=foo&data2=bar');
Reagards
Tom
Simply use callback.
obj.doPost('getSomeData.php', '&data1=foo&data2=bar', function(data) {
result = data;
});
It is impossible to turn an asynchronous function into a synchronous one.
It simply cannot be done.
Instead, you must pass a callback to your function and receive the "return value" in async fashion.
In theory though, you could write some code to block your function from returning until some condition is met (ie, until the async operation is complete), but that would also require the program to be able do other things on another thread while the block is executing, which is probably not possible in node. And even if it were, it would be a world class antipattern and crime against node.js and all things evented and probably summon a velociraptor.
Conclusion: Learn how to work with asynchronous code. Also, you might be interested in reading this question/answer from yesterday (or at least the answer; the question is not very well phrased).