JavaScript如何利用Promise控制并發(fā)請(qǐng)求個(gè)數(shù)
一、場(chǎng)景
假設(shè)現(xiàn)在有這么一種場(chǎng)景:現(xiàn)有 30 個(gè)異步請(qǐng)求需要發(fā)送,但由于某些原因,我們必須將同一時(shí)刻并發(fā)請(qǐng)求數(shù)量控制在 5 個(gè)以內(nèi),同時(shí)還要盡可能快速的拿到響應(yīng)結(jié)果。
如圖所示:
上圖這樣的排隊(duì)和并發(fā)請(qǐng)求的場(chǎng)景基本類似,窗口只有三個(gè),人超過三個(gè)之后,后面的人只能排隊(duì)了。
二、串行和并行
- 串行:一個(gè)異步請(qǐng)求完了之后在進(jìn)行下一個(gè)請(qǐng)求
- 并行:多個(gè)異步請(qǐng)求同時(shí)進(jìn)行
串行舉例:
var p = function () { return new Promise(function (resolve, reject) { setTimeout(() => { console.log("1000"); resolve(); }, 1000); }); }; var p1 = function () { return new Promise(function (resolve, reject) { setTimeout(() => { console.log("2000"); resolve(); }, 2000); }); }; var p2 = function () { return new Promise(function (resolve, reject) { setTimeout(() => { console.log("3000"); resolve(); }, 3000); }); }; p() .then(() => { return p1(); }) .then(() => { return p2(); }) .then(() => { console.log("end"); });
如示例,串行會(huì)從上到下依次執(zhí)行對(duì)應(yīng)接口請(qǐng)求。
并行舉例:
通常,我們?cè)谛枰WC代碼在多個(gè)異步處理之后執(zhí)行,會(huì)用到:
Promise.all((promises: [])).then((fun: function));
Promise.all可以保證,promises數(shù)組中所有promise對(duì)象都達(dá)到resolve狀態(tài),才執(zhí)行then回調(diào)。
var promises = function () { return [1000, 2000, 3000].map((current) => { return new Promise(function (resolve, reject) { setTimeout(() => { console.log(current); }, current); }); }); }; Promise.all(promises()).then(() => { console.log("end"); });
這時(shí)候考慮一個(gè)場(chǎng)景:
如果你的promises數(shù)組中每個(gè)對(duì)象都是http請(qǐng)求,而這樣的對(duì)象有幾十萬個(gè)。
那么會(huì)出現(xiàn)的情況是,你在瞬間發(fā)出幾十萬個(gè)http請(qǐng)求,這樣很有可能導(dǎo)致堆積了無數(shù)調(diào)用棧導(dǎo)致內(nèi)存溢出。
這時(shí)候,我們就需要考慮對(duì)Promise.all做并發(fā)限制。
Promise.all并發(fā)限制指的是,每個(gè)時(shí)刻并發(fā)執(zhí)行的promise數(shù)量是固定的,最終的執(zhí)行結(jié)果還是保持與原來的Promise.all一致。
三、代碼實(shí)現(xiàn)
整體采用遞歸調(diào)用來實(shí)現(xiàn):最初發(fā)送的請(qǐng)求數(shù)量上限為允許的最大值,并且這些請(qǐng)求中的每一個(gè)都應(yīng)該在完成時(shí)繼續(xù)遞歸發(fā)送,通過傳入的索引來確定了urls里面具體是那個(gè)URL,保證最后輸出的順序不會(huì)亂,而是依次輸出。
代碼:
function multiRequest(urls, maxNum) { const len = urls.length; // 請(qǐng)求總數(shù)量 const res = new Array(len).fill(0); // 請(qǐng)求結(jié)果數(shù)組 let sendCount = 0; // 已發(fā)送的請(qǐng)求數(shù)量 let finishCount = 0; // 已完成的請(qǐng)求數(shù)量 return new Promise((resolve, reject) => { // 首先發(fā)送 maxNum 個(gè)請(qǐng)求,注意:請(qǐng)求數(shù)可能小于 maxNum,所以也要滿足條件2 // 同步的 創(chuàng)建maxNum個(gè)next并行請(qǐng)求 然后才去執(zhí)行異步的fetch 所以一上來就有5個(gè)next并行執(zhí)行 while (sendCount < maxNum && sendCount < len) { next(); } function next () { let current = sendCount ++; // 當(dāng)前發(fā)送的請(qǐng)求數(shù)量,后加一 保存當(dāng)前請(qǐng)求url的位置 // 遞歸出口 if (finishCount >= len) { // 如果所有請(qǐng)求完成,則解決掉 Promise,終止遞歸 resolve(res); return; } const url = urls[current]; fetch(url).then(result => { finishCount ++; res[current] = result; if (current < len) { // 如果請(qǐng)求沒有發(fā)送完,繼續(xù)發(fā)送請(qǐng)求 next(); } }, err => { finishCount ++; res[current] = err; if (current < len) { // 如果請(qǐng)求沒有發(fā)送完,繼續(xù)發(fā)送請(qǐng)求 next(); } }); } }); }
總結(jié):
代碼在while循環(huán)處創(chuàng)建了maxNum個(gè)"請(qǐng)求窗口"來進(jìn)行請(qǐng)求,從而達(dá)到并行效果,然后next函數(shù)中進(jìn)行異步請(qǐng)求,然后通過在.then里面進(jìn)行遞歸進(jìn)行新請(qǐng)求的調(diào)用,實(shí)現(xiàn)"一個(gè)窗口只進(jìn)行一個(gè)請(qǐng)求,當(dāng)這個(gè)請(qǐng)求執(zhí)行完成后才進(jìn)行下一個(gè)請(qǐng)求"(每個(gè)窗口串行執(zhí)行,maxNum個(gè)窗口并行執(zhí)行)。
到此這篇關(guān)于JavaScript如何利用Promise控制并發(fā)請(qǐng)求個(gè)數(shù)的文章就介紹到這了,更多相關(guān)js用Promise控制并發(fā)請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
參考鏈接
相關(guān)文章
javascript實(shí)現(xiàn)校驗(yàn)文件上傳控件實(shí)例
這篇文章主要介紹了javascript實(shí)現(xiàn)校驗(yàn)文件上傳控件,實(shí)例分析了javascript檢測(cè)上傳文件類型是否為圖片的功能,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04基于JavaScript實(shí)現(xiàn)游戲購(gòu)物車效果詳解
這篇文章主要介紹了如何利用JavaScript實(shí)現(xiàn)游戲購(gòu)物車效果,文中的實(shí)現(xiàn)代碼講解詳細(xì),對(duì)我們學(xué)習(xí)JavaScript有一定幫助,需要的可以參考一下2022-03-03基于slideout.js實(shí)現(xiàn)移動(dòng)端側(cè)邊欄滑動(dòng)特效
這篇文章主要為大家詳細(xì)介紹了基于slideout.js實(shí)現(xiàn)移動(dòng)端側(cè)邊欄滑動(dòng)特效,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11javascript實(shí)現(xiàn)在指定元素中垂直水平居中
當(dāng)談到網(wǎng)頁的布局中,居中問題一直得不到很有效的解決,居中通常是相對(duì)于某一個(gè)元素的,比如我們經(jīng)常所說的屏幕居中的問題,我們了解父元素的信息越多,我們就越能更加容易的實(shí)現(xiàn)居中布局。下面我們通過具體的實(shí)例來看看javascript如何來實(shí)現(xiàn)垂直水平居中2015-09-09js實(shí)現(xiàn)頁面跳轉(zhuǎn)的五種方法推薦
下面小編就為大家?guī)硪黄猨s實(shí)現(xiàn)頁面跳轉(zhuǎn)的五種方法推薦。小編覺得挺不錯(cuò)的?,F(xiàn)在分享給大家,讓大家參考一下2016-03-03Add Formatted Data to a Spreadsheet
Add Formatted Data to a Spreadsheet...2007-06-06Gulp實(shí)現(xiàn)靜態(tài)網(wǎng)頁模塊化的方法詳解
眾所周知Gulp.js 是一個(gè)自動(dòng)化構(gòu)建工具,開發(fā)者可以使用它在項(xiàng)目開發(fā)過程中自動(dòng)執(zhí)行常見任務(wù)。下面這篇文章主要給大家介紹了關(guān)于Gulp實(shí)現(xiàn)靜態(tài)網(wǎng)頁模塊化的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。2018-01-01