在 Meteor 项目中,经常会有客户端使用 Meteor.call 方法去调用服务端的一个方法,并等待该方法返回。通常情况下,服务端的方法只需要 return 后,客户端使用回调函数就可以访问到 return 的值了。但如果服务端同样调用了一个异步执行的函数,那么此时就无法判断服务端的异步函数是否已经执行完毕,返回结果就会出现不准确的情况。如下所示:

服务端代码:

setWechatMenu: function (appId, appSecret, jsonMenu) {
console.log(jsonMenu);
// 创建微信菜单
var url = “https://api.weixin.qq.com/cgi-bin/token?” +
“grant_type=client_credential&appid=” +
appId + “&secret=” + appSecret;

Meteor.http.get(url, { timeout: 30000 }, function(err, result) {
var jsonContent = JSON.parse(result.content);
var accessToken = jsonContent.access_token;
var url = “https://api.weixin.qq.com/cgi-bin/menu/create?access\_token=“ + accessToken;
// post 创建菜单
var response = Meteor.http.post(url, { data: jsonMenu }, function(err, result) {
console.log(‘Content: ‘ + result.content);
});
return response;
});
}

客户端代码:

Meteor.call(“setWechatMenu”, appId, appSecret, json, function(error, result) {
if (!error) {
console.log(“result :”, result);
};
});

上面的例子中,我们在客户端使用 Meteor.call 方法调用了一个服务端的函数,等待服务端的异步函数 http.post 返回内容,但你会发现结果并不是那么理想,客户端在打印 result 的时候结果是 undefined。这是因为服务端 http.get 和 http.post 都使用了异步回调的方式取得返回值,实际这两个函数在调用时立即就返回了。而客户端也是立即就接收到了服务器的返回,并没有真正等到 http.get 和 http.post 执行完毕。 我开始天真的认为,只要在客户端使用同步方法调用 Meteor.call 不就可以了吗?

var result = Meteor.call(“setWechatMenu”, appId, appSecret, json);

但结果告诉我,这样是不行的,后来在 Meteor 官方查询文档也说到,这样的调用是有不确定性的,也就是跟使用异步回调处理是没什么区别的。而这种同步的方法仅在服务端(On the server)是有效的: 2015-11-15_113548 为了解决这种问题,Meteor 的 github issues 专门有一篇文章是介绍如何解决类似问题的:https://gist.github.com/possibilities/3443021 其中最简单的一个方法如下:

setWechatMenu: function (appId, appSecret, jsonMenu) {
// 创建一个 future
var Future = Npm.require(‘fibers/future’);
var fut = new Future();

console.log(jsonMenu);
// 创建微信菜单
var url = “https://api.weixin.qq.com/cgi-bin/token?” +
“grant_type=client_credential&appid=” +
appId + “&secret=” + appSecret;

Meteor.http.get(url, { timeout: 30000 }, function(err, result) {
var jsonContent = JSON.parse(result.content);
var accessToken = jsonContent.access_token;
var url = “https://api.weixin.qq.com/cgi-bin/menu/create?access\_token=“ + accessToken;
// post 创建菜单
var response = Meteor.http.post(url, { data: jsonMenu }, function(err, result) {
console.log(‘Content: ‘ + result.content);
});
// 将 response 传递给 fut 的成员对象
fut.return(response);
});
// 等待 fut 被赋值后再返回
return fut.wait();
}

更多的方法可以参考上面 issues 链接。