[面试题] JS中同时发起多个网络请求,如何接收到最先返回就停止所有请求并返回该值?

假设有三个网络请求 A、B 和 C,它们的返回值分别为 "A"、"B" 和 "C",但由于网络延迟的原因,它们的返回顺序是不确定的。请使用 Promise.race() 实现一个函数,它在接收到任意一个请求的返回值后就停止所有请求并返回该值。


本文参与了SegmentFault 思否面试闯关挑战赛,欢迎正在阅读的你也加入。
阅读 2.2k
2 个回答

要使用Promise.race()实现一个函数,使得在接收到任意一个请求的返回值后就停止所有请求并返回该值,我们可以将每个请求与一个"取消请求"操作组合在一起。这里有一个示例实现:

// 模拟网络请求
function fetchApi(apiName, timeout) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(apiName);
    }, timeout);
  });
}

// 取消请求的信号
class CancellationToken {
  constructor() {
    this.isCanceled = false;
  }

  cancel() {
    this.isCanceled = true;
  }
}

// 可取消的网络请求
function cancelableFetchApi(apiName, timeout, cancellationToken) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (!cancellationToken.isCanceled) {
        resolve(apiName);
      } else {
        reject(new Error('Request canceled'));
      }
    }, timeout);
  });
}

async function raceToFirstResponse() {
  const cancellationToken = new CancellationToken();

  const requestA = cancelableFetchApi('A', 300, cancellationToken);
  const requestB = cancelableFetchApi('B', 200, cancellationToken);
  const requestC = cancelableFetchApi('C', 100, cancellationToken);

  try {
    const result = await Promise.race([requestA, requestB, requestC]);
    cancellationToken.cancel(); // 取消其他请求
    console.log('First result:', result);
    return result;
  } catch (error) {
    console.error('Error:', error.message);
  }
}

raceToFirstResponse();

在这个示例中,我们首先创建了一个fetchApi函数来模拟网络请求。然后,我们定义了一个CancellationToken类,它用于表示取消请求的信号。接着,我们创建了一个cancelableFetchApi函数,它接收一个额外的参数cancellationToken,用于检查请求是否已被取消。

raceToFirstResponse函数中,我们使用Promise.race()等待所有请求的竞争,并在竞争结束后取消其他未完成的请求。这样,我们就实现了在接收到任意一个请求的返回值后停止所有请求并返回该值的功能。

使用 Promise.race 这个API,把所有网络请求 Promisify 之后都放入到一个数组当中,传递给 Promise.race
当其中有任意一个请求返回后, Promise.race 就会返回该请求返回的 Promise 对象,再通过调用 .then() 方法就可以获取到返回结果了。

例如说:

function test() {
  Promise.race([
    fetch('https://api.example.com/A'), 
    fetch('https://api.example.com/B'), 
    fetch('https://api.example.com/C')
  ]).then(res => {
    console.log(res.result)
  });
}
本文参与了SegmentFault 思否面试闯关挑战赛,欢迎正在阅读的你也加入。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题