功能类似于Promise.some / any,用于未知数量的承诺

时间:2017-06-30 15:33:45

标签: javascript node.js promise bluebird ecmascript-7

我正在node.js(V8.1.3)中创建一个脚本,该脚本查看来自多个API的类似JSON数据并比较这些值。更确切地说,我正在研究不同股票的不同市场价格(实际上是加密货币)。

目前,我正在使用promise.all等待各个API的所有响应。

let fetchedJSON =
        await Promise.all([getJSON(settings1), getJSON(settings2), getJSON(settings3) ... ]); 

但是,即使只有一个承诺拒绝并出现错误,Promise.all也会抛出错误。在bluebird docos中有一个名为Promise.some的函数,这几乎就是我想要的。据我所知,它需要一系列承诺并解决两个最快的解决方案,否则(如果少于2个promises解决)会抛出错误。

问题在于,首先,我不希望最快的两个承诺被解决为它返回的,我希望任何成功的承诺被退回,只要有这似乎是Promise.any所做的,除非最小数为1.(我要求最低数量为2)

其次,我不知道我将等待多少Promise(换句话说,我不知道有多少API会从我那里请求数据)。它可能只有2或可能是30.这取决于用户输入。

目前写这篇文章在我看来,可能只有一种方法可以获得一个计数为2的promise.any,这将是最简单的解决方案。这可能吗?

是的,不确定标题是否真的总结了这个问题。请建议编辑标题:)

编辑:我编写脚本的另一种方法是,前两个加载的API开始计算并推送到浏览器,然后是后续加载和计算的每个JSON。这样我就不会等待所有Promise在我开始计算数据并将结果传递到前端之前完成。这个功能是否适用于其他情况呢?

我的意思是这样的:

并行请求JSON ......

| ----- ------ JSON1 |

| --- JSON-失败--- | >捕获错误>做错事。不会影响下一个结果。

| ------- ------- JSON2 | >至少满足2个结果>计算JSON>浏览器。

| ------- JSON3 --------- | >计算JSON>浏览器。

4 个答案:

答案 0 :(得分:5)

then所有承诺如此失败,将其传递给Promise.all,并在最终.then中过滤成功结果。

这样的事情:

function some( promises, count = 1 ){

   const wrapped = promises.map( promise => promise.then(value => ({ success: true, value }), () => ({ success: false })) );
   return Promise.all( wrapped ).then(function(results){
      const successful = results.filter(result => result.success);
      if( successful.length < count )
         throw new Error("Only " + successful.length + " resolved.")
      return successful.map(result => result.value);
   });

}

答案 1 :(得分:2)

考虑到您要求实施反模式,这可能有些笨拙,但您可以强迫每个承诺解决:

async function fetchAllJSON(settingsArray) {
  let fetchedJSON = await Promise.all(
    settingsArray.map((settings) => {
      // force rejected ajax to always resolve
      return getJSON(settings).then((data) => {
        // initial processing
        return { success: true, data }
      }).catch((error) => {
        // error handling
        return { success, false, error }
      })
    })
  ).then((unfilteredArray) => {
    // only keep successful promises
    return dataArray.filter(({ success }) => success)
  })
  // do the rest of your processing here
  // with fetchedJSON containing array of data
}

答案 2 :(得分:1)

您可以使用Promise.allSettled([])。所不同的是,在所有promise都成立后,无论成功还是失败,allSettled都将返回对象数组。然后找到成功的人,无论您需要什么。

  let resArr = await Promise.allSettled(userNamesArr.map(user=>this.authenticateUserPassword(user,password)));
        return resArr.find(e=>e.status!="rejected");

或返回resArr.find(e => e.status ==“ fulfilled”)。

答案 3 :(得分:0)

其他答案的缺点是必须等待所有的诺言得以解决,而理想情况下,.some将在任何(N)个诺言通过谓词后立即返回。

let anyNPromises = (promises, predicate = a => a, n = 1) => new Promise(async resolve => {
	promises.forEach(async p => predicate(await p) && !--n && resolve(true));
	await Promise.all(promises);
	resolve(false);
});

let atLeast2NumbersGreaterThan5 = promises => anyNPromises(promises, a => a > 5, 2);

atLeast2NumbersGreaterThan5([
	Promise.resolve(5),
	Promise.resolve(3),
	Promise.resolve(10),
	Promise.resolve(11)]
).then(a => console.log('5, 3, 10, 11', a)); // true

atLeast2NumbersGreaterThan5([
	Promise.resolve(5),
	Promise.resolve(3),
	Promise.resolve(10),
	Promise.resolve(-43)]
).then(a => console.log('5, 3, 10, -43', a)); // false

atLeast2NumbersGreaterThan5([
	Promise.resolve(5),
	Promise.resolve(3),
	new Promise(() => 'never resolved'),
	Promise.resolve(10),
	Promise.resolve(11)]
).then(a => console.log('5, 3, unresolved, 10, 11', a)); // true