【问题标题】:Wait an event to resolve a promise等待一个事件来解决一个承诺
【发布时间】:2018-03-30 18:55:11
【问题描述】:

我正在使用一个 node.js 模块,它有一个没有回调的方法。取而代之的是,当该方法完成时会触发一个事件。我想解决一个承诺,使用该事件作为回调来确保该方法已成功完成。

array.lenght on promise 可以是 X。所以,我需要“听到”X 次事件来确保我所有方法都已成功完成 这不是问题,我只是告诉你我知道这可能会发生

事件:

tf2.on('craftingComplete', function(recipe, itemsGained){
  if(recipe == -1){
  console.log('CRAFT FAILED')
  }
  else{
        countOfCraft++;
    console.log('Craft completed! Got a new Item  #'+itemsGained);
  }
})

承诺:

const craftWepsByClass = function(array, heroClass){
        return new Promise(function (resolve, reject){

            if(array.length < 2){
                console.log('Done crafting weps of '+heroClass);
                return resolve();
            }
            else{
                for (var i = 0; i < array.length; i+=2) {
                    tf2.craft([array[i].id, array[i+1].id]); // <--- this is the module method witouth callback
                }
        return resolve(); // <---- I want resolve this, when all tf2.craft() has been completed. I need 'hear' event many times as array.length
            }   

        })
}

【问题讨论】:

  • tf2.craft() 是否返回 Promise?请注意,Promise 只能被解析或拒绝一次。
  • @guest271314 不要。 tf2.craft() 什么都不返回;
  • that has a method without callbacks. on 是一个回调.. :)
  • @Keith 但是它与单个方法调用是分开的,这是一个主要问题
  • 我知道我说英语很糟糕。我会尽力而为。当我调用 tf2.craft 时,我需要检查事件“craftingComplete”是否已触发多次。任何可能的 ID 或工艺是否失败都无关紧要。我需要知道 tf2.craft 是否已完成,以及为什么要检查“craftingComplete”事件

标签: javascript node.js events promise resolve


【解决方案1】:

首先让我们承诺制作:

function craft(elem){
 //do whatever
 return Promise((resolve,reject) => 
  tf2.on('craftingComplete', (recipe,itemsGained) => 
   if( recipe !== -1 ){
     resolve(recipe, itemsGained);
   }else{
    reject("unsuccessful");
   }
  })
);
}

因此,为了制作倍数,我们将数组映射到 Promise 并使用 Promise.all:

Promise.all( array.map( craft ) )
 .then(_=>"all done!")

【讨论】:

  • resolvereject 来自哪里?它们似乎不在范围内。
  • @Keith Promise 执行器函数未包含在原始答案的代码中
  • @Keith - 这个答案的原始版本没有在任何定义的范围内显示resolve()。答案已被编辑。您可以查看版本历史记录,查看原始答案并查看 Bergi 的响应是在最近的编辑之前,该编辑在承诺执行器函数中显示它。
  • @Jonasw reject() 的使用是否与问题中的模式相对应? OP 期待被拒绝吗?注意,未处理; Promise 或在countOfCraft++ 发布增量countOfCraft
  • 我认为您不想在每次调用 craftWepsByClass 时都添加一个新的 craftingComplete 处理程序 - 并且永远不要删除它们。
【解决方案2】:

我需要检查事件“craftingComplete”是否像我一样触发了很多次 调用 tf2.craft。任何可能的 ID 或工艺是否失败都无关紧要。 我需要知道 tf2.craft 是否已经完成以及为什么要检查 “制作完成”事件

鉴于我们知道ifor 循环内将递增i += 2,其中i 小于array.length,我们可以在for 之前创建一个等于该数字的变量循环并将i 与事件处理程序中的数字进行比较

const craftWepsByClass = function(array, heroClass) {
  return new Promise(function(resolve, reject) {

    var countCraft = 0;
    var j = 0;
    var n = 0;
    for (; n < array.length; n += 2);

    tf2.on('craftingComplete', function(recipe, itemsGained) {
      if (recipe == -1) {
        console.log('CRAFT FAILED')
      } else {
        countOfCraft++;
        console.log('Craft completed! Got a new Item  #' + itemsGained);
        if (j === n) {
          resolve(["complete", craftCount])
        }
      }
    })

    if (array.length < 2) {
      console.log('Done crafting weps of ' + heroClass);
      return resolve();
    } else {
      try {
        for (var i = 0; i < array.length; i += 2, j += 2) {
          tf2.craft([array[i].id, array[i + 1].id]);
        }
      } catch (err) {
        console.error("catch", err);
        throw err
      }
    }

  })
}

craftWepsByClass(array, heroClass)
.then(function(data) {
  console.log(data[0], data[1])
})
.catch(function(err) {
  console.error(".catch", err)
})

【讨论】:

  • 我不认为你想在每次调用 craftWepsByClass 时添加一个新的 craftingComplete 处理程序。
  • @Bergi 可能不会。对于每个新的tf2 实例,craftWepsByClass() 只会被调用一次。 OP 现在有几种可能的解决方案可供选择。
  • "craftWepsByClass() 对于每个新的tf2 实例只会被调用一次。" - 你怎么知道的?为什么你甚至认为有多个 tf2 实例?
  • @Bergi “你怎么知道的?为什么你甚至认为有多个tf2实例?”同样的评估适用于“每次@987654338 @ 被调用。”
  • 这就是整个问题:它可以发生,所以你必须为此设计你的函数。如果您做出(不常见但可以想象的)假设 craftWepsByClass 最多被调用一次,您应该在代码中使用断言明确说明,或者至少在文档中添加注释或通知。我错过了你帖子中的任何一个。
【解决方案3】:

如果事件的触发顺序与引发它们的相应 craft() 调用的顺序相同,则可以使用队列:

var queue = []; // for the tf2 instance
function getNextTf2Event() {
  return new Promise(resolve => {
    queue.push(resolve);
  });
}
tf2.on('craftingComplete', function(recipe, itemsGained) {
  var resolve = queue.shift();
  if (recipe == -1) {
    resolve(Promise.reject(new Error('CRAFT FAILED')));
  } else {
    resolve(itemsGained);
  }
});

function craftWepsByClass(array, heroClass) {
  var promises = [];
  for (var i = 1; i < array.length; i += 2) {
    promises.push(getNextTf2Event().then(itemsGained => {
      console.log('Craft completed! Got a new Item  #'+itemsGained);
      // return itemsGained;
    }));
    tf2.craft([array[i-1].id, array[i].id]);
  }
  return Promise.all(promises).then(allItemsGained => {
    console.log('Done crafting weps of '+heroClass);
    return …;
  });
}

如果您对事件的顺序一无所知,并且可能有多个并发调用 craftWepsByClass,则无法避免全局计数器(即链接到 tf2 实例的计数器)。缺点是例如在两个重叠调用 a = craftWepsByClass(…), b = craftWepsByClass() 中,a 承诺在第二个调用的所有制作完成之前不会得到解决。

var waiting = []; // for the tf2 instance
var runningCraftings = 0;
tf2.on('craftingComplete', function(recipe, itemsGained) {
  if (--runningCraftings == 0) {
    for (var resolve of waiting) {
      resolve();
    }
    waiting.length = 0;
  }
  if (recipe == -1) {
    console.log('CRAFT FAILED')
  } else {
    console.log('Craft completed! Got a new Item  #'+itemsGained);
  }
});

function craftWepsByClass(array, heroClass) {
  for (var i = 1; i < array.length; i += 2) {
    runningCraftings++;
    tf2.craft([array[i-1].id, array[i].id]);
  }
  return (runningCraftings == 0
    ? Promise.resolve()
    : new Promise(resolve => {
        waiting.push(resolve);
      })
  ).then(() => {
    console.log('Done crafting weps of '+heroClass);
  });
}

当然,在这两种解决方案中,您必须 100% 确定每次调用 craft() 都会导致一个事件。

【讨论】:

  • 好消息,不应该有。
【解决方案4】:

您可以查看event-as-promise 包。它连续将事件转换为 Promise,直到您完成所有事件处理。

当与async/await 结合使用时,您可以轻松编写带有事件的 for-loop 或 while-loop。例如,我们要处理data事件,直到它返回null

const eventAsPromise = new EventAsPromise();

emitter.on('data', eventAsPromise.eventListener);

let done;

while (!done) {
  const result = await eventAsPromise.upcoming();

  // Some code to process the event result
  process(result);

  // Mark done when no more results
  done = !result;
}

emitter.removeListener('data', eventAsPromise.eventListener);

如果您精通生成器功能,使用它可能看起来更简单。

const eventAsPromise = new EventAsPromise();

emitter.on('data', eventAsPromise.eventListener);

for (let promise of eventAsPromise) {
  const result = await promise;

  // Some code to process the event result
  process(result);

  // Stop when no more results
  if (!result) {
    break;
  }
}

emitter.removeListener('data', eventAsPromise.eventListener);

【讨论】:

    猜你喜欢
    • 2015-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-04
    • 1970-01-01
    • 2019-04-29
    • 2018-03-13
    • 1970-01-01
    相关资源
    最近更新 更多