Ecmascript 6 你如何实施一个;“竞争成功”;助手,给我一份承诺清单?

Ecmascript 6 你如何实施一个;“竞争成功”;助手,给我一份承诺清单?,ecmascript-6,es6-promise,Ecmascript 6,Es6 Promise,我对ES6 Promise API中的某些内容感到困惑。我可以看到并发提交多个异步作业并在第一次成功时“解析”的清晰用例。例如,这将适用于多个等效服务器可用的情况,但有些服务器可能已停机,而另一些服务器负载过重且速度较慢,因此我的目标是从第一个成功的服务器获得响应,而忽略其余的服务器(是的,我知道对于客户端来说,从服务器的角度来看,这是一种令人讨厌的行为方式,但对于最终用户来说,这是一种很好的方式;) 然而,据我所见,我可以使用“全部”或“竞态”行为。“全部”行为似乎要等到所有请求完成,这意味着

我对ES6 Promise API中的某些内容感到困惑。我可以看到并发提交多个异步作业并在第一次成功时“解析”的清晰用例。例如,这将适用于多个等效服务器可用的情况,但有些服务器可能已停机,而另一些服务器负载过重且速度较慢,因此我的目标是从第一个成功的服务器获得响应,而忽略其余的服务器(是的,我知道对于客户端来说,从服务器的角度来看,这是一种令人讨厌的行为方式,但对于最终用户来说,这是一种很好的方式;)

然而,据我所见,我可以使用“全部”或“竞态”行为。“全部”行为似乎要等到所有请求完成,这意味着我必须等待最慢的请求,即使服务器已经完成(事实上,我可能不得不等待超时,在这种情况下,这将是一场灾难。)“竞然而,行为似乎给了我第一个完成的机会,如果这碰巧是一个失败,那也是一场灾难

API中是否有允许“raceToSuccess”行为的东西,或者我必须手工构建它。就此而言,我将如何手工构建它


作为补充说明,我在Java 8 CompletableFuture中发现了同样的难题,它似乎是一个非常并行的API。那么,我是否在哲学层面上遗漏了一些东西?

您可以自己轻松编写

function raceToSuccess(promises) {
  return new Promise(
    resolve => 
      promises.forEach(
        promise => 
          promise.then(resolve)
      )
  );
}
这将启动所有承诺,当任何承诺成功时,将使用其值解析新承诺。失败的承诺将被忽略。后续成功的承诺不会导致任何事情发生,因为新承诺已被解析。请注意,如果输入承诺均未解析,则生成的承诺将永远不会解析或拒绝

以下是一个修改版本,如果所有输入承诺均被拒绝,则返回被拒绝的承诺:

function raceToSuccess(promises) {
  let numRejected = 0;

  return new Promise(
    (resolve, reject) => 
      promises.forEach(
        promise => 
          promise . 
            then(resolve) .
            catch(
              () => {
                if (++numRejected === promises.length) reject(); 
              }
           )
       )
  );
}
// ignores any rejects except if all promises rejects
Promise.firstResolve = function (promises) {
    return new Promise(function (fulfil, reject) {
        var rejectCount = 0;
        promises.forEach(function (promise) {
            promise.then(fulfil, () => {
                rejectCount++;
                if(rejectCount == promises.length) {
                    reject('All promises were rejected');
                } 
            });
        });
    });
};
我喜欢@loganfsmyth的方法;你可能应该对它的概念清晰性投赞成票。下面是它的一个变体:

function invertPromise(promise) {
  return new Promise(
    (resolve, reject) => 
      promise.then(reject, resolve)
  );
}

function raceToSuccess(promises) {
  return invertPromise(
    Promise.all(
      promises.map(invertPromise)));
}
另一个想法是将失败的承诺转化为既不能解决也不能拒绝的承诺(换句话说,是永久未决的),然后使用
Promise.race

function pendingPromise()      { return new Promise(() => { }); }
function killRejected(promise) { return promise.catch(pendingPromise); }

function raceToSuccess(promises) {
  return Promise.race(promises.map(killRejected));
}

您可能喜欢也可能不喜欢这种行为。如果输入承诺都没有实现,则返回的承诺将永远不会实现或拒绝。也可能永久挂起的承诺不会得到GC'd,或者某些引擎最终可能会抱怨这些承诺。

这是一个经典的示例,在这个示例中,反转逻辑会使其更加清晰。您可以在这种情况下,“种族”是指你希望你的拒绝行为实际上是成功行为

function oneSuccess(promises){
  return Promise.all(promises.map(p => {
    // If a request fails, count that as a resolution so it will keep
    // waiting for other possible successes. If a request succeeds,
    // treat it as a rejection so Promise.all immediately bails out.
    return p.then(
      val => Promise.reject(val),
      err => Promise.resolve(err)
    );
  })).then(
    // If '.all' resolved, we've just got an array of errors.
    errors => Promise.reject(errors),
    // If '.all' rejected, we've got the result we wanted.
    val => Promise.resolve(val)
  );
}

老话题,但这里是我的条目;它本质上是@loganfsmyth的解决方案,但还需要一些检查,以符合以下方面建立的约定:

  • 空数组作为输入(同步)返回已解析的承诺
  • 数组中的非承诺项将导致第一个此类项用作解析值
Promise.any=a=>{
返回!长度?
承诺。决心():
我答应你(
e=>(e.then的类型!=='function')?
承诺。拒绝(e):
e、 然后(
结果=>承诺.拒绝(结果),
失败=>Promise.resolve(失败)
)
)).那么(
allRejected=>Promise.Rejected(allRejected),
firstResolved=>Promise.resolve(firstResolved)
);
};
//测试。。。
函数延迟(超时、结果、被拒绝){
返回新承诺((解决、拒绝)=>{
设置超时(
()=>拒绝?拒绝(结果):解决(结果),
超时);
});
}
答应我([
延迟(800,'a'),
延迟(500,'b'),
延迟(250,'c',真)
]).然后(e=>{
log('First resolved(应为b):',e);
});
答应我([
延迟(800,'a',正确),
延迟(500,'b',正确),
延迟(250,'c',真)
])。然后(null,e=>{
log('所有被拒绝(预期失败数组):',e);
});
答应我([
延迟(800,'a'),
延迟(500,'b'),
延迟(250,'c',正确),
“d”,
“e”
]).然后(e=>{
log('First non-promise(预期为d):',e);
});
//因为这是唯一需要同步解决的情况,
//它的输出应该出现在其他输出之前
承诺。任何([])。然后(e=>{
log('空输入(预期未定义):',e);

});
我用超时扩展了@loganfsmyth方法,并编写了一个小函数:

  • 履行所有承诺
  • 等待承诺成功的时间不超过指定的时间(options.timeOutMs)
  • 返回第一个成功的
在以下代码段中,您可以对其进行测试:

const firstthattcompletedsuccessfullyes6=(选项)=>{
//回报你的第一个承诺
constOneSuccess=(promises)=>Promise.all(promises.map)(p=>{
//如果请求失败,则将其视为一个解决方案,这样它将保留
//等待其他可能的成功。如果请求成功,
//将其视为拒绝,因此承诺。所有人立即退出。
返回p(
(val)=>{返回承诺。拒绝(val);},
(err)=>{return Promise.resolve(err);}
);
})
).那么(
//如果“.all”被解决,我们就得到了一系列错误。
(错误)=>{返回承诺.拒绝(错误);},
//如果“.all”被拒绝,我们就得到了想要的结果。
(val)=>{return Promise.resolve(val);}
);
//如果先超时,则返回承诺或重新承诺
const timeoutPromise=(毫秒,承诺)=>新承诺(函数(解析,拒绝){
setTimeout(()=>reject(新错误('timeout')),毫秒;
承诺。然后(决定,拒绝);
});
if(options.subsystem.length<1){
return Promise.reject('参数错误,未指定子系统
// fastest promise to end, but is a reject (gets ignored)
var promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject("foo")
    }, 100);
})

// fastest promise to resolve (wins the race)
var promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("bar")
    }, 200);
})

// Another, slower resolve (gets ignored)
var promise3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("baz")
    }, 300);
})

Promise.firstResolve([promise1, promise2, promise3])
    .then((res) => {
        console.log(res) // "bar"
    })
    .catch(err => {
        console.log(err) // "All promises were rejected" (if all promises were to fail)
    })
// ignores any and all rejects
Promise.firstResolve = function (promises) {
    return new Promise(function (fulfil) {
        promises.forEach(function (promise) {
            promise.then(fulfil, () => {});
        });
    });
};
const PromiseRiceSuccess = <T = unknown>(promises: Promise<T>[]) => {
  let done: (reason?: T) => void;
  const waitEndAllPromises = new Promise((resolve, reject) => done = reject);
  const waitCatchs = promise => Promise.resolve(promise).catch(() => waitEndAllPromises);

  Promise.allSettled(promises).then(r => done(r));

  return Promise.race(promises.map(waitCatchs));
};
PromiseRiceSuccess([
  Promise.reject(1),
  new Promise((r) => setTimeout(() => r(2), 4000)),
]);
// 2

PromiseRiceSuccess([
  Promise.reject(1),
  new Promise((resolve, reject) => setTimeout(() => reject(2), 4000)),
]);
// Uncaught (in promise) (2) [{…}, {…}]
// assume getApi returns a Promise

const promises = [
  getApi('url1'),
  getApi('url2'),
  getApi('url3'),
  getApi('url4'),
];
Promise.any(promises)
  .then((result) => {
    // result will contain the resolve value of the first Promise to resolve
  })
  .catch((err) => {
    // Every Promise rejected
  });