面试官问:如果有100个请求,你如何使用Promise控制并发?

简介: 面试官问:如果有100个请求,你如何使用Promise控制并发?

开篇

在现代Web开发中,异步请求已经成为了必不可少的一部分。然而,当我们需要同时处理多个请求时,如何避免请求之间的冲突和混乱呢?这就是今天我们要探讨的话题——如何使用Promise控制并发请求。

JavaScript中可以通过Promise.all()Promise.race()async/await等不同方式来实现对异步并发任务的控制。以下是一种使用Promise.all()方法实现并发控制的示例:

Promise.all()

const urls = ["url1", "url2", ... ,"url100"]; 
const maxConcurrentNum = 10; // 最大并发数 
// 数组分块,chunk表示每批次数量,返回数组二维数组 
function chunk(arr, chunk) { 
  let result = []; 
  for (let i = 0, len = arr.length; i < len; i += chunk) { 
    result.push(arr.slice(i, i + chunk)); 
   } 
   return result; 
 }
// 异步请求方法 
function fetchUrl(url) { 
  return new Promise((resolve, reject) => { 
    fetch(url) 
      .then(res => resolve(res)) 
      .catch(err => reject(err)); 
     }); 
   }
// 对url数组进行分块处理
const chunkedUrls = chunk(urls, maxConcurrentNum);
(async function () {
  try {
    for (let urls of chunkedUrls) {
      const promises = urls.map(url => fetchUrl(url));
      // 等待所有promises完成执行,并将结果存入results数组中
      const results = await Promise.all(promises);
      console.log('results:', results);
    }
  } catch (err) {
   console.error(err);
  }
})();

以上代码通过将数组分成多个数目相等的小数组,每次最多只开启maxConcurrentNum个并发请求,以此来控制并发数量。每当一组请求完成后再发送新的一批请求,可以实现对异步任务的并发控制。

Promise.race()

以下是使用Promise.race()方法来控制并发的示例代码:

const promiselist = [];
for (let i = 0; i < 100; i++) {
  const promise = fetch(`https://example.com/data${i}.json`);
  promiselist.push(promise);
}
Promise.race(promiselist)
  .then(response => {
    // handle the fastest response here
  })
  .catch(error => {
    console.error(error);
  });

async/await

以下是使用async/await方式控制并发请求的示例代码:

async function getData() {
  const promiselist = [];
  for (let i = 0; i < 100; i++) {
    const promise = fetch(`https://example.com/data${i}.json`);
    promiselist.push(promise);
  }
  const responses = await Promise.all(promiselist);
  for (const response of responses) {
    // handle each response here 
  }
}
getData().catch(error => {
  console.error(error);
});

在上面的代码中,我们首先创建了一个async函数,并在该函数中使用for循环来发送所有的请求,并将每个请求的Promise对象存储在一个数组中。 然后,我们使用await关键字来异步等待所有Promise对象都被解决,并将解决值存储在一个数组中。 最后,我们在处理每个响应时对数组进行迭代。

如果我们只需要等待最快的请求,我们可以使用Promise.race()方法,并将其包装在一个async函数中。 这种方法与使用Promise.all()的方式相似,只需使用不同的Promise方法即可。

以下是使用async/await方式控制并发请求的示例代码,其中使用Promise.race()方法:

async function getData() {
  const promiselist = [];
  for (let i = 0; i < 100; i++) {
    const promise = fetch(`https://example.com/data${i}.json`);
    promiselist.push(promise);
  }
  const response = await Promise.race(promiselist);
  // handle the fastest response here
}
getData().catch(error => {
  console.error(error);
});

在上述代码中,我们使用async函数来生成Promise对象,然后使用Promise.race()方法等待最快的解决Promise对象,并处理其解决值。

除了Promise.all()Promise.race()以及async/await等方法外,还有其他用于控制并发请求的可行方法,例如:

  1. 手动控制计数器
    可以使用变量来手动计数,以控制请求并发数。例如,在循环中,当计数器达到最大并发请求数时,将其用于等待请求完成,然后递增计数器以允许下一个请求。
    以下是手动控制计数器的示例代码:
function getData() {
 const limit = 5; // maximum concurrent requests
 const dataUrls = ['https://example.com/data1.json', 
                   'https://example.com/data2.json',
                   'https://example.com/data3.json',
                   'https://example.com/data4.json',
                   'https://example.com/data5.json',
                   'https://example.com/data6.json'];
 let counter = 0;
 const getDataPromise = dataUrl => {
   return new Promise((resolve, reject) => {
     fetch(dataUrl)
       .then(response => {
         counter--;
         resolve(response);
       })
       .catch(error => {
         counter--;
         reject(error);
       });
   });
 };
 const getDataPromises = dataUrls.map(dataUrl => {
   if (counter < limit) {
     counter++;
     return getDataPromise(dataUrl);
   } else {
     return new Promise(resolve => {
       const interval = setInterval(() => {
         if (counter < limit) {
           counter++;
           clearInterval(interval);
           resolve(getDataPromise(dataUrl));
         }
       }, 100);
     });
   }
 });
 Promise.all(getDataPromises)
   .then(responses => {
     for (const response of responses) {
       // handle each response here
     }
   })
   .catch(error => {
     console.error(error);
   });
}
getData();

在上面的代码中,我们手动地使用计数器来控制最大并发请求数,然后使用setInterval函数来等待可用的请求槽位。

  1. 使用第三方库

此外,还有一些第三方库可以使用,例如async.jsp-limit等。p-limit是一个专门用于控制Promise并发的小型库。可以在p-limit文档中找到更多信息和示例。

总结

通过掌握Promise的使用,我们可以轻松应对并发请求,让我们的Web应用更加流畅,用户更加满意。所以,别让并发请求成为你的噩梦,让Promise来帮你解决吧!

前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

相关文章
|
6天前
|
存储 前端开发
除了 Promise.all(),还有哪些方法可以处理异步并发操作?
在上述示例中,`concurrentPromises` 函数接受一个Promise数组和最大并发数作为参数,通过手动控制并发执行的Promise数量,实现了对异步操作的并发控制,并在所有Promise完成后返回结果数组。
|
6天前
|
前端开发 JavaScript
如何使用 Promise 处理异步并发操作?
通过使用 `Promise.all()` 和 `Promise.race()` 方法,可以灵活地处理各种异步并发操作,根据不同的业务需求选择合适的方法来提高代码的性能和效率,同时也使异步代码的逻辑更加清晰和易于维护。
|
3天前
|
前端开发 数据处理
如何使用 Promise.all() 处理异步并发操作?
使用 `Promise.all()` 可以方便地处理多个异步并发操作,提高代码的执行效率和可读性,同时通过统一的 `.catch()` 方法能够有效地处理异步操作中的错误,确保程序的稳定性。
|
6天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
3月前
|
缓存 前端开发 中间件
[go 面试] 前端请求到后端API的中间件流程解析
[go 面试] 前端请求到后端API的中间件流程解析
|
1月前
|
Java 调度 Android开发
Android面试题之Kotlin中async 和 await实现并发的原理和面试总结
本文首发于公众号“AntDream”,详细解析了Kotlin协程中`async`与`await`的原理及其非阻塞特性,并提供了相关面试题及答案。协程作为轻量级线程,由Kotlin运行时库管理,`async`用于启动协程并返回`Deferred`对象,`await`则用于等待该对象完成并获取结果。文章还探讨了协程与传统线程的区别,并展示了如何取消协程任务及正确释放资源。
23 0
|
3月前
|
Java 程序员 调度
面试准备-并发
面试准备-并发
|
3月前
|
消息中间件 Java 中间件
复盘女朋友面试4个月的并发面试题
该文章主要复盘了关于并发的面试题,包括线程池的使用场景、原理、参数合理化设置,以及ThreadLocal、volatile、synchronized关键字的使用场景和原理,还介绍了juc并发工具包中aqs的原理,强调在面试中要将自己理解的点与面试官讲透。
复盘女朋友面试4个月的并发面试题
|
3月前
|
JavaScript 前端开发 Java
面试官:假如有几十个请求,如何去控制并发?
面试官:假如有几十个请求,如何去控制并发?
|
3月前
|
安全 Go 调度
[go 面试] 深入理解并发控制:掌握锁的精髓
[go 面试] 深入理解并发控制:掌握锁的精髓

热门文章

最新文章