ES6承诺等待K out N承诺解决

时间:2018-01-06 16:02:44

标签: javascript ecmascript-6

var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, 'one'); 
});
var p2 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 2000, 'two'); 
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'three');
});

Promise.all([p1,p2,p3]).then(values => { 
  console.log(values);
}, reason => {
  console.log(reason)
});

我如何等待2条承诺完成? Promise.race()等待一个承诺完成。

修改

我有许多承诺,我想要实现的是等待前k个承诺得到解决而不是触发某些事件。假设 k< Ñ

编辑 - 2

我确信k号承诺将在n号承诺中成功解决

6 个答案:

答案 0 :(得分:9)

注意: Bluebird有一个built-in helper function,它正好用于此目的,并且与我下面的waitForN方法具有或多或少相同的行为,因此该选项始终可用)

我不相信ES6承诺有一个优雅的内置方式来做到这一点,但你可以定义以下相对较短的 现在有点长的帮助函数照顾这个。

编辑添加了一个额外的帮助函数,其中包含结果中成功承诺的索引。

修改:如果拒绝次数达到无法成功解决的程度,则更新以拒绝。

修改更新为更优雅地使用promises,如果它是可迭代的,并检查结果应立即解决的边缘情况(例如,如果n === 0

function waitForN(n, promises) {
    let resolved = [];
    let failCount = 0;
    let pCount = 0;

    return new Promise((resolve, reject) => {
        const checkSuccess = () => {
            if (resolved.length >= n) { resolve(resolved); }
        };
        const checkFailure = () => {
            if (failCount + n > pCount) {
                reject(new Error(`Impossible to resolve successfully. n = ${n}, promise count = ${pCount}, failure count = ${failCount}`));
            }
        };

        for (let p of promises) {
            pCount += 1;

            Promise.resolve(p).then(r => {
                if (resolved.length < n) { resolved.push(r); }
                checkSuccess();
            }, () => {
                failCount += 1;
                checkFailure();
            });
        }

        checkFailure();
        checkSuccess();
    });
}

const waitForNWithIndices = (n, promises) =>
    waitForN(n, promises.map((p, index) => 
        Promise.resolve(p).then(result => ({ index, result }))
    ));

var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, 'one'); 
});
var p2 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 2000, 'two'); 
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1500, 'four');
});

waitForN(2, [p1,p2,p3,p4]).then(values => { 
  console.log(values);
}, reason => {
  console.log(reason)
});

waitForNWithIndices(2, [p1,p2,p3,p4]).then(values => { 
  console.log(values);
}, reason => {
  console.log(reason)
});

答案 1 :(得分:0)

你可以使用更高阶的函数来烘焙n然后运行等待n完成的所有承诺

var p1 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 1000, 'one'); 
});
var p2 = new Promise((resolve, reject) => { 
  setTimeout(resolve, 2000, 'two'); 
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'three');
});

const raceN = n => (...promises) => {
  const resolved = []
  return new Promise((res, rej) =>
    promises.forEach(promise => 
      promise.then(x => {
        resolved.push(x)
        if (resolved.length === n)
          res(resolved)
      })
      .catch(rej)
    )
  )
}

const race2 = raceN(2)
race2(p1, p2, p3)
  .then(results => console.log(results))
  
race2(Promise.resolve('resolved'), Promise.reject('rejected'))
  .then(results => console.log(results))
  .catch(reason => console.log(reason))

答案 2 :(得分:0)

有点晚了,但也许会为你做这件事:

const first = howMany => (promises,resolved=[],rejected=[]) => {
  if(promises.length-rejected.length<howMany){
    return Promise.reject([resolved,rejected]);
  }
  if(resolved.length===howMany){
    return Promise.resolve(resolved);
  }
  if(resolved.length===0&&rejected.length===0){
    promises=promises.map(
      (p,index)=>Promise.resolve(p)
        .then(resolve=>[resolve,index])
        .catch(err=>Promise.reject([err,index]))
    );
  }
  return Promise.race(promises)
  .then(
    ([resolve,index])=>
      first(howMany)(
        promises.filter(
          (p,i)=>i!==index
        ),
        resolved.concat([resolve]),
        rejected
      )
  )
  .catch(
    ([err,index])=>
    first(howMany)(
      promises.filter(
        (p,i)=>i!==index
      ),
      resolved,
      rejected.concat([err])
    )  
  );
};

const first2 = first(2);
first2([p1,p2,p3])
.then(
  result=>//you will have an array of 2 with resolve value(s)
)
.catch(
  err=>//you will have 2 arrays, resolved ones and errors
)

答案 3 :(得分:0)

编辑:已更新以匹配问题修改。

当抽象限制你的表现力时,很容易自己动手。

此代码很简短,可以轻松地重新编写,以便在旧版ES3环境中运行。

您还可以通过添加代码来轻松扩展其实用性,以取消未解决的超时。这就是当你不把所有东西交给图书馆时所得到的;更简单,更有能力。

function raceN(n, fns, resolve, reject) {
  const res = [], rej = [];
  let halt = false;

  for (const [idx, fn] of fns.entries()) {
    fn(data => update(res, data, idx), data => update(rej, data, idx));
  }
  function update(arr, data, idx) {
    if (halt) return;
    arr.push({idx, data});

    if ((halt=res.length >= n)) resolve(res);
    else if ((halt=rej.length > fns.length - n)) reject(rej);
  }
}

<强> 样本:

function raceN(n, fns, resolve, reject) {
  const res = [], rej = [];
  let halt = false;

  for (const [idx, fn] of fns.entries()) {
    fn(data => update(res, data, idx), data => update(rej, data, idx));
  }
  function update(arr, data, idx) {
    if (halt) return;
    arr.push({idx, data});

    if ((halt=res.length >= n)) resolve(res);
    else if ((halt=rej.length > fns.length - n)) reject(rej);
  }
}



function rand() { return Math.ceil(Math.random() * 5000) }

var fns = [(resolve, reject) => { 
  setTimeout(resolve, rand(), 'one'); 
}, 
(resolve, reject) => { 
  setTimeout(resolve, rand(), 'two'); 
}, 
(resolve, reject) => {
  setTimeout(reject, rand(), 'three');
}];

raceN(2, fns, values => { 
  console.log("SUCCESS:", values);
}, reason => {
  console.log("REJECT:", reason)
});

如果您保证至少n个元素会成功,那么它会变得更短更简单。

function raceN(n, fns, resolve, reject) {
  const res = [];
  for (const [idx, fn] of fns.entries()) {
    fn(data => res.length < n && res.push({idx, data}) == n) && resolve(res),
       data => reject({idx, data}));
  }
}

<强> 样本:

function raceN(n, fns, resolve, reject) {
  const res = [];

  for (const [idx, fn] of fns.entries()) {
    fn(data => res.length < n && res.push({idx, data}) == n) && resolve(res),
       data => reject({idx, data}));
  }
}



function rand() { return Math.ceil(Math.random() * 4000) }

var fns = [(resolve, reject) => { 
  setTimeout(resolve, rand(), 'one'); 
}, 
(resolve, reject) => { 
  setTimeout(resolve, rand(), 'two'); 
}, 
(resolve, reject) => {
  setTimeout(reject, rand(), 'three');
}];

raceN(2, fns, values => { 
  console.log("SUCCESS:", values);
}, reason => {
  console.log("REJECTING:", reason)
});

答案 4 :(得分:0)

鉴于我对JLRishe的答案有很多评论,我还会发布我的功能略有不同的版本:

function any(k, iterable) {
    const results = [];
    let fullfilled = 0, rejected = 0, n = 0;

    return new Promise((resolve, reject) => {
        k = Math.max(0, Math.floor(k));
        function check() {
            if (fulfilled == k)
                resolve(results);
            else if (rejected + k == n + 1)
                reject(new Error(`No ${k} of ${n} got fulfilled`));
        }

        for (const thenable of iterable) {
            const i = n++;
            Promise.resolve(thenable).then(res => {
                if (fulfilled++ < k) results[i] = res;
                check();
            }, () => {
                rejected++;
                check();
            });
        }
        check();
    });
}

答案 5 :(得分:0)

另一个想法可能是在满足条件时使用Promise.reject()将其缩短,同时捕获可能出现的错误,可能如下;

var p1 = new Promise((v, x) =>  setTimeout(v, 1000, 'one')),
    p2 = new Promise((v, x) =>  setTimeout(v, 5000, 'two')),
    p3 = new Promise((v, x) =>  setTimeout(v, 1500, 'three')),
    pn = (n, ps, k = 0, r = []) => Promise.all(ps.map(p => p.then(v => (k === n - 1 ? Promise.reject(r.concat(v))
                                                                                    : (++k, r.push(v))))))
                                          .catch(r => Array.isArray(r) ? r : Promise.reject(r));

pn(2,[p1,p2,p3]).then(rs => console.log(rs))
                .catch(e => console.log(e));