在前端开发中,经常会涉及到并发请求的场景,例如批量处理数据、分页加载数据等。而 Promise 是处理异步请求的一种优秀方式。然而,如果并发请求数量过多,可能会导致服务器压力过大,甚至造成应用崩溃。因此,我们需要对 Promise 进行并发控制,并考虑超时处理策略。
Promise 并发控制
为了控制 Promise 的并发数量,我们可以使用 Promise.all() 方法。这个方法可以接收一个 Promise 数组,然后在所有 Promise 完成时返回一个新的 Promise 实例,其 resolved value 是一个数组,包含传递给 Promise.all() 的每个 Promise 的 resolved value。例如:
const promises = [promise1, promise2, promise3]; Promise.all(promises).then(values => { console.log(values); }).catch(error => { console.error(error); });
这个例子中,当 promise1、promise2 和 promise3 都完成后,Promise.all() 返回一个新的 Promise 实例,其 resolved value 是一个数组,包含这三个 Promise 的 resolved value。
但是,这种方式无法控制并发数量,如果需要限制并发数量,可以使用第三方库如 bluebird、q 等。这些库都提供了一些有用的方法来控制并发数量,例如:
const promises = [promise1, promise2, promise3]; Promise.map(promises, task, { concurrency: 2 }).then(values => { console.log(values); }).catch(error => { console.error(error); });
这个例子中,Promise.map() 方法接收三个参数,第一个参数是要执行的 Promise 数组,第二个参数是一个函数,用于处理每个 Promise 的 resolved value,第三个参数是一个对象,可以用来设置并发数量。
另外,为了更好地控制并发数量,我们可以将 Promise 封装成一个函数,并使用 async/await 来组织代码。例如:
// javascriptcn.com 代码示例 async function processTasks(tasks, concurrency) { const results = []; const running = []; for (const task of tasks) { if (running.length >= concurrency) { await Promise.race(running); } const promise = task(); running.push(promise); promise.then(result => { results.push(result); running.splice(running.indexOf(promise), 1); }); } await Promise.all(running); return results; }
这个函数接收两个参数,一个是要处理的任务数组,另一个是并发数量。在函数内部,我们使用一个 results 数组来存储每个 Promise 的 resolved value,使用一个 running 数组来存储当前正在执行的 Promise。然后,我们使用 for...of 循环遍历任务数组,如果 running 数组长度已经达到并发数量,就使用 Promise.race() 选择一个最先完成的 Promise,并等待其完成。接着,将当前任务封装成一个 Promise,存储到 running 数组中,并在 Promise 完成时将 resolved value 推入 results 数组中,同时从 running 数组中移除该 Promise。最后,使用 Promise.all() 等待所有 Promise 完成,并返回 results 数组。
Promise 超时处理
在处理异步请求时,可能会发生请求超时的情况。为了避免这种情况,我们需要对 Promise 增加超时处理策略。首先,我们需要使用 Promise.race() 方法来等待 Promise 完成,同时监听 Promise 是否超时。例如:
Promise.race([promise, timeoutPromise]).then(result => { console.log(result); }).catch(error => { console.error(error); });
在这个例子中,我们使用 Promise.race() 方法来等待一个 Promise 和一个超时 Promise。如果 Promise 在指定时间内完成,就会返回其 resolved value,否则将会返回 timeoutPromise 的 rejected reason。
时间 Promise 可以使用 Promise 和 setTimeout() 方法组合实现,例如:
function timeoutPromise(ms) { return new Promise((resolve, reject) => { setTimeout(() => { reject(new Error(`Timeout after ${ms}ms`)); }, ms); }); }
这个函数接收一个毫秒数,返回一个 Promise,延时指定时间后 reject 一个错误。具体使用方式如下:
const promise = fetch('/data'); const timeout = timeoutPromise(5000); Promise.race([promise, timeout]).then(result => { console.log(result); }).catch(error => { console.error(error); });
这个例子中,我们使用 fetch() 函数获取数据,并设置一个超时时间为 5 秒。如果请求在 5 秒内完成,就会返回其 resolved value;否则,会返回一个 rejected reason,错误信息为 "Timeout after 5000ms"。
最后,为了更好地组织这些代码,我们可以使用封装函数的方式,例如:
async function requestWithTimeout(url, options, timeout = 5000) { const fetchPromise = fetch(url, options); const timeoutPromise = timeoutPromise(timeout); const result = await Promise.race([fetchPromise, timeoutPromise]); return result; }
这个函数接收三个参数,一个是请求的 URL,另一个是请求的 options 配置,最后一个是超时时间。然后,我们使用 fetch() 函数获取数据,并设置一个超时时间,使用 Promise.race() 方法监听超时和请求完成情况,最后返回 resolved value。
通过这样的方式,我们就可以方便地处理 Promise 的超时情况,避免请求时间过长导致的应用阻塞等问题。
总结
在本文中,我们介绍了如何使用 Promise 控制并发数量,并增加超时处理策略。值得注意的是,这些技术还可以与其他技术如 async/await、Generator 等配合使用,以更好地组织和控制我们的异步请求代码。在实际开发过程中,我们需要根据具体需求和场景灵活选择合适的技术和库,以便处理好异步请求相关的问题。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/653d2cc37d4982a6eb7135fe