azl397985856 / fe-interview

宇宙最强的前端面试指南 (https://lucifer.ren/fe-interview)
Apache License 2.0
2.83k stars 260 forks source link

【每日一题】- 2020-05-12 - 实现 ES2020 - Promise.any #125

Closed azl397985856 closed 4 years ago

azl397985856 commented 4 years ago

Promise.any()是ES2020的一个标准,目前是stage3阶段,它是一个Promise的一个静态方法,这个方法会在其实例列表中至少有一个resolve的时候触发。

eg:

const result = await Promise.any([
  Promise.resolve(33),
  new Promise(resolve => setTimeout(() => resolve(66), 1000)),
  Promise.reject(new Error('an error'))
]);
// result = 33;

请实现之。

PS:

corejs已经实现了,大家也可以参考一下。

suukii commented 4 years ago

just implement it by my intuition, no parameter validation or anything

Promise.any = promises => {
  return new Promise((resolve, reject) => {
    let hasOneResolved = false
    let remaining = promises.length
    const errors = []
    for (let index in promises) {
      promises[index].then(data => {
        if (hasOneResolved) return
        hasOneResolved = true
        resolve(data)
      }, err => {
        if (hasOneResolved) return
        remaining--
        errors[index] = err
        remaining || reject(errors)
      })
    }
  })
}
feikerwu commented 4 years ago
/**
 * 如果传入的参数是一个空的可迭代对象,则返回一个 已完成(already resolved) 状态的 Promise。
 * 如果传入的参数不包含任何 promise,则返回一个 异步完成 (asynchronously resolved)的 Promise。
 * @param promises
 */
function any<T>(
  promises: (T | PromiseLike<T>)[] | Iterable<T | PromiseLike<T>>
): Promise<T | void> {
  let promiseSize: number = 0;
  let selfResolve: (value: T) => void;
  let selfReject: (reason: unknown) => void;
  let rejectedReasons: Array<unknown> = [];

  let anyPromise: Promise<T> = new Promise((resolve, reject) => {
    selfResolve = (value: T) => {
      resolve(value);
    };
    selfReject = (reason: unknown) => {
      if (rejectedReasons.length === promiseSize) {
        reject(rejectedReasons);
      } else {
        rejectedReasons.push(reason);
      }
    };
  });

  for (let p of promises) {
    if (thenable(p)) {
      promiseSize += 1;
      p.then(selfResolve, selfReject);
    }
  }

  if (promiseSize === 0) {
    return Promise.resolve();
  }

  return anyPromise;
}

function thenable(p: any): p is Promise<unknown> {
  return typeof p.then === 'function';
}
azl397985856 commented 4 years ago

随便写的,仅供参考。

Promise.any = ps => new Promise((resolve, reject) => {
 let cnt = 0;
 ps.map(p => p.then(resolve).catch((err) => ++cnt === ps.length && reject(err)))
})

测试:

const resolveTimer = ms => new Promise(r => setTimeout(() => r(`${ms}$ elapsed`), ms))
const rejectTimer =  ms => new Promise((_, r) => setTimeout(() => r(`${ms}$elapse`), ms))

any([timer(1000), timer(2000), timer(3000)]).then(console.log)
any([rejectTimer(1000), timer(2000), timer(3000)]).then(console.log)
any([rejectTimer(1000), rejectTimer(2000), rejectTimer(3000)]).then(console.log).catch(console.log)
stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.