如何等待流星调用响应,然后在javascript中执行其他语句?



我在 meteor 中的客户端事件中有两个流星调用,我想一个接一个地执行。但是当我调试流程时,剂量并没有按照我想要的方式进行。

客户端.js

Meteor.call('methodCall',param1,param2,param3,function (error, result) {
if (error) 
console.log(error.reason);
Session.set("xyz",result);
});
var abc=Session.get("xyz");
Meteor.call('methodCall',abc,param2,param3,function (error, result) {
if (error) 
console.log(error.reason);
console.log("result: "+result);
Session.set("cdf",result);
}); 
var pqr=Session.get("cdf");

如您所见,这是我想要按顺序运行的代码,即一个接一个。但是当我调试代码时,我发现执行顺序是:

1. Meteor will be called
3. session.get("xyz") return undefined.
4. Meteor will be called
6. session.get("cdf") return undefined.
2. session.set() will have results as value.
5. session.get() will not have any value.

第二个 meteor.call() 将无法成功执行,因为第一个参数在步骤 2 之前执行的步骤 3 没有任何值。那么有什么方法可以实现这一点并等待流星呼叫完成以执行下一个指令吗?

我已经对这种情况的各种选择进行了一些研究,因为这里的其他人可能也已经面对过这种情况。

选项 A - 客户端中的嵌套调用

第一个也是最明显的一个是进行嵌套调用。这意味着在回调中收到结果后调用下一个函数。

// level 1
Meteor.call('methodCall', param1, param2, param3, function (error, result) {
// level 2
if (error) console.log(error.reason);
Session.set("xyz",result);
Meteor.call('methodCall',result, param2, param3, function (error, result) {
// level 3...
if (error) console.log(error.reason);
console.log("result: "+result);
Session.set("cdf",result);
}); 
});

优点:经典的js方式,不需要花哨的新概念,服务器方法坚持简单的逻辑,而客户端死亡复杂的工作

缺点:丑陋,会引起混乱,有时难以调试

要求:Template.autorunTracker.autorun以被动地从Session捕获更改。


选项 B - 包装异步

许多人可能已经发现这种方法是将异步代码结构化为同步代码的第一选择。

纤程(和利用纤程的 wrapAsync)使代码看起来只是同步的,但执行的性质仍然是异步的。这与承诺的工作方式或异步/等待的工作方式相同。

优点:在单一环境中功能强大

缺点:不能与流星通话一起使用

要求:一根光纤要磨合

Meteor.call 的问题

但是,您无法使用此功能轻松调用 Meteor 方法。请考虑以下代码

const param1 = "param1";
const param2 = "param2";
const param3 = "param3";

const asyncCall = Meteor.wrapAsync(Meteor.call);
const result1 = asyncCall("methodCall", param1, param2, param3);
// result1 will be undefined

为了进一步解释,我将引用文档:

在客户端上,如果您不传递回调并且您不在 存根,调用将返回未定义,您将无法获得 方法的返回值。那是因为客户端没有 光纤,所以实际上没有任何方法可以在遥控器上阻塞 方法的执行。

摘要:Meteor.wrapAsync不得与Meteor.call一起使用。


选项 C - 捆绑在一种方法中

除了尝试创建同步的 meteor 调用序列,您还可以将所有参数和逻辑提供给单个服务器方法,该方法返回一个保留所有返回值的对象:

客户端.js

const param1 = "param1";
const param2 = "param2";
const param3 = "param3";

Meteor.call('methodCall', param1, param2, param3, function (err, result) {
const xyz = result.xyz;
const cdf = result.cdf;
});

服务器.js

function _methodCall(p1, p2, p3) {
// ... 
return result;
}
Meteor.methods({
'methodCall'(p1, p2, p3) {
const result1 = _methodCall(p1, p2, p3);
const result2 = _methodCall(result1, p2, p3);
return {
xyz: result1,
cdf: result2,
}
}
})

这将创建一个顺序执行(通过遵循您在问题中提供的顺序逻辑),并在捆绑对象中返回其所有结果。

优点:根据需要顺序,一个请求 - 所有结果 缺点:一个额外的方法要测试,可能会在方法之间引入紧密耦合,返回对象可能会变得很大且复杂,无法解析 clinet 要求:良好的方法设计意识

如果我找到其他选项,我会将它们添加到这篇文章中。

一种方法是稍微重新组织你的代码。

Meteor.call('methodCall',param1,param2,param3,function (error, result) 
{
if (error) console.log(error.reason);
Session.set("xyz",result);
var abc=Session.get("xyz");
Meteor.call('methodCall',abc,param2,param3,function (error, result) 
{
if (error) console.log(error.reason);
console.log("result: "+result);
Session.set("cdf",result);
var pqr=Session.get("cdf");
});
});

你必须使用承诺,例如未来的纤维

在服务器上

Meteor.methods({
'methodCall': function(params...){
var future = new Future();
try{
your code...
future.return(result)
catch(e){
future.throw(e)
}finally{
return future.wait();
}
},
})

在客户端上

Meteor.call('methodCall',params...,(err,res)=>{
if(err){
console.log(err);
}else{
console.log(res);
}
});

参考 https://github.com/jagi/meteor-astronomy/issues/562 的链接

对不起,我不喜欢任何解决方案。如何将 Meteor.call 回调转换为承诺?

const meteorPromiseCall = (method: string, ...args: any[]) =>
new Promise((resolve, reject) => {
Meteor.call(method, ...args, (err: any, res: any) => {
if (err) reject(err);
else resolve(res);
});
});

和使用示例:

const Dashboards = () => {
const [data, setData] = useState(null);

const readData = async () => {
// Waiting to all Meteor.calls
const res = await Promise.all([
meteorPromiseCall(
"reports.activitiesReport",
DateTime.now().startOf("day").minus({ day: 30 }).toJSDate(),
DateTime.now().startOf("day").toJSDate(),
),
meteorPromiseCall(
"reports.activitiesReport2",
DateTime.now().startOf("day").minus({ day: 30 }).toJSDate(),
DateTime.now().startOf("day").toJSDate(),
),
meteorPromiseCall(
"reports.activitiesReport3",
DateTime.now().startOf("day").minus({ day: 30 }).toJSDate(),
DateTime.now().startOf("day").toJSDate(),
),
]);
setData(res[0]);
};
useEffect(() => {
readData();
}, []);
if (!data) return <div>Loading...</div>;
return (...)

相关内容

最新更新