Как дождаться ответа на вызов метеора, а затем выполнить другие операторы в javascript?

У меня есть два вызова метеора в клиентских событиях в метеоре, где я хочу выполнить один за другим. Но когда я отладил, поток не пошел так, как я хотел бы.

client.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() не будет успешно выполнен, потому что 1-й параметр не будет иметь никакого значения, так как шаг 3 выполняется до шага 2. Итак, есть ли способ добиться этого и дождаться завершения вызова метеора для выполнения следующих инструкций?


person Snkini    schedule 20.03.2018    source источник


Ответы (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.autorun или Tracker.autorun для оперативного захвата изменений из Session.


Вариант B — обернуть асинхронно

Многие, возможно, уже сочли этот метод лучшим выбором для структурирования асинхронного кода в синхронизирующий.

Волокна (и wrapAsync, использующий волокна) делают код только выглядящим синхронизированным, но характер выполнения остается асинхронным. Это работает так же, как работают Promises или как работают async/await.

Плюсы: мощный в единой среде.

Минусы: нельзя использовать с Meteor.call.

Требуется: волокно для запуска

Проблема с 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

Для дальнейшего объяснения я приведу документацию:

На клиенте, если вы не передадите обратный вызов и не находитесь внутри заглушки, call вернет неопределенное значение, и у вас не будет возможности получить возвращаемое значение метода. Это связано с тем, что у клиента нет волокон, поэтому на самом деле он не может заблокировать удаленное выполнение метода.

Резюме: Meteor.wrapAsync нельзя использовать вместе с Meteor.call.


Вариант C — объединение в один метод

Вместо того, чтобы пытаться создать синхронизированную последовательность вызовов метеора, вы также можете предоставить все параметры и логику одному серверному методу, который возвращает объект, который хранит все возвращаемые значения:

client.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 Требования: хороший смысл для разработки методов

Если я найду другие варианты, я добавлю их в этот пост.

person Jankapunkt    schedule 21.03.2018

Один из способов — немного реорганизовать код.

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");
  });
});
person gihef    schedule 20.03.2018

Вы должны использовать обещание, например, будущие волокна

на сервере

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

person Nikhil Goswami    schedule 12.06.2018