Promise.any 在 JS 上的可能实现

Possible implementation of Promise.any on JS

在 Promise.race 中承诺 returns 作为主要承诺 returns。在 Promise.all returns 中,所有承诺都解决了,但持续了一个问题。如果所有承诺中的任何一个被拒绝,所有其他承诺都将被拒绝。

取而代之的是 proposal 对应 Promise.any,returns 每个承诺都单独存在,彼此独立,在拒绝时短路。

const logAfterWait = (seconds) => new Promise((resolve, reject) => {
    return setTimeout(() => resolve(console.log(`${time} time passed`)), seconds)
}) 

const watingList = [
    logAfterWait(convertToSeconds(10)),
    logAfterWait(convertToSeconds(30)),
    logAfterWait(convertToSeconds(5))
]

const logReading = async (fn) => {
    console.log(`${time}: reading file`)
    await fn()
}

const readFiles = (files) => Promise.all(watingList.map(logReading))
    .catch((error) => new Error(error))

这里的问题是Promise.all上的事件循环映射原因块上的事件循环块,同时返回每个结果,与预期结果不同,即5、10、 30 秒。

我可以在 waitingList.map 上避免这种情况吗?

您可以利用 Promise.race 通过创建永不稳定的 Promise 形成幺半群这一事实:

const empty = x => new Promise((res, rej) => x); // never settling promise

const ps = [
  Promise.reject(1).catch(empty),
  Promise.resolve(2).catch(empty),
  Promise.resolve(3).catch(empty)];

Promise.race(ps)
  .then(console.log); // 2

您需要为数组中的每个 Promise 附加一个 catch 处理程序。您或许可以创建一个实用函数来为您执行此操作。

你可能会想到这样的事情:

// a solution might just be not using async/await
const any = (promises) => new Promise((resolve, reject) => {
  let errors = [];
  let resolved;
  
  const onFulfill = (value) => {
    // skip if already resolved
    if (resolved) { return; }
    resolved = true;
    
    // resolve with the first available value
    resolve(value);
  };
  
  const onError = (error) => {
    // skip if already resolved
    if (resolved) { return; }
    
    // collect error
    errors = errors.concat(error);
    
    // reject promise combinator if all promises are failed
    if (errors.length === promises.length) {
      reject(errors);
    }
  };
  
  return promises.forEach((promise) => promise.then(
    onFulfill,
    onError,
  ));
});

const sleep = (ms) => new Promise(r => setTimeout(() => r(ms), ms));
const err = (ms) => sleep(ms).then(() => Promise.reject(ms));

// it would log 2000, since it is the first to resolve
any([sleep(3000), err(100), sleep(2000)]).then(console.info)


// it would an array of 2 failures
any([err(50), err(60)]).catch(console.error)


the block of IO

请注意,javascript 中没有任何 IO 块,线程在等待 promise 被解决时可以自由处理任何其他任务。

据此,我得出了一个结论。我们创建了一个解析器,它是一个 Either monad(不是 Either monad 的纯实现),它 returns [err, response] 在 map 函数上。

catch 块是避免未处理的承诺拒绝警告所必需的。

const time = () => `${new Date().getHours()}:${new Date().getMinutes()}:${new Date().getSeconds()}`;

const sleep = (ms, pNumber) => new Promise((resolve, reject) => {
    return pNumber < 3
        ? setTimeout(() => resolve(console.log(`${time()} time passed`)), ms)
        : reject(null)
    }).catch(null)


Promise.prototype.resolver = async (promise) => {
    this._result = await Promise.all([promise])[0];
    
    return this._result == null
        ? ["The time flies", promise]
        : [null, promise]
}

const watingList = [
    Promise.resolver(sleep(0, 0).catch(console.error)),
    Promise.resolver(sleep(3000, 1).catch(console.error)),
    Promise.resolver(sleep(5000, 2).catch(console.error)),
    Promise.resolver(sleep(5000, 3).catch(console.error))
]

const logReading = (list) => {
    return list.map(p => p.then(console.log(`${time()}: reading file`))
        .catch(console.log))
}

((read) => logReading(read))(watingList)

PS: time 函数因计算时间而与预期不同。