简体   繁体   English

等待一个事件来解决一个承诺

[英]Wait an event to resolve a promise

I am using a node.js module that has a method without callbacks.我正在使用一个 node.js 模块,它有一个没有回调的方法。 Instead of it, has an event that fires when that method has finished.取而代之的是,有一个在该方法完成时触发的事件。 I want resolve a promise, using that event as callback securing me that method has been completed succesfully.我想解决一个承诺,使用该事件作为回调确保该方法已成功完成。

array.lenght on promise can be X. So, I need 'hear' X times the event to secure me that all methods has completed succesfully <-- This is not the problem, I am just telling you that I know this could happen array.lenght on promise 可以是 X。所以,我需要“听到”X 次事件来确保所有方法都成功完成<--这不是问题,我只是告诉你我知道这可能发生

Event :事件 :

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

Promise:承诺:

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
            }   

        })
}

At first lets promisify the crafting:首先让我们承诺制作:

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

So to craft multiples then, we map our array to promises and use Promise.all:因此,为了制作倍数,我们将数组映射到 promises 并使用 Promise.all:

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

If the events will be fired in the same order as the respective craft() calls that caused them, you can use a queue:如果事件的触发顺序与引起它们的各个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 …;
  });
}

If you don't know anything about the order of the events, and there can be multiple concurrent calls to craftWepsByClass , you cannot avoid a global counter (ie one linked to the tf2 instance).如果您对事件的顺序一无所知,并且可能有多个对craftWepsByClass并发调用,则无法避免全局计数器(即链接到tf2实例的计数器)。 The downside is that eg in two overlapping calls a = craftWepsByClass(…), b = craftWepsByClass() the a promise won't be resolved until all crafting of the second call is completed.缺点是,例如在两个重叠的调用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);
  });
}

Of course in both solutions you must be 100% certain that each call to craft() causes exactly one event.当然,在这两种解决方案中,您必须 100% 确定每次调用craft()都会导致一个事件。

You can look at the event-as-promise package.您可以查看event-as-promise包。 It convert events into Promise continuously until you are done with all the event processing.不断将事件转换为 Promise,直到您完成所有事件处理。

When combined with async / await , you can write for-loop or while-loop easily with events.async / await结合使用时,您可以使用事件轻松编写 for-loop 或 while-loop。 For example, we want to process data event until it return null .例如,我们要处理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);

If you are proficient with generator function, it may looks a bit simpler with this.如果您精通生成器功能,那么它可能看起来更简单一些。

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);

I need check if event 'craftingComplete' has fired many times as I call tf2.craft.当我调用 tf2.craft 时,我需要检查事件 'craftingComplete' 是否已多次触发。 Doesnt matters any posible ID or if craft has failed.任何可能的 ID 或工艺是否失败都无关紧要。 I need to know if tf2.craft has finished and only why is checking 'craftingComplete' event我需要知道 tf2.craft 是否已完成,以及为什么要检查“craftingComplete”事件

Given that we know i within for loop will be incremented i += 2 where i is less than .length of array , we can create a variable equal to that number before the for loop and compare i to the number within event handler由于我们知道ifor循环将递增i += 2 ,其中i小于.lengtharray ,我们可以创建一个变量等于该数字前的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)
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM