串行按序發送100個Ajax請求

串行按序發送100個Ajax請求

在面試中不止一次被問到如何串行的發送n個網絡請求,回答的時候總是迷迷糊糊。因爲平常都是在看別人的文章,這方面沒有實際動手實踐過。但是看完別人的文章,印象總是不太深刻。這次希望自己一步一步可以通過多種方法來實現串行連續發送100個網絡請求。

1. 編寫一個node服務用來接收ajax請求

爲了能夠直觀的看到效果,這裏使用了nodejs的koa框架來寫了一個最簡單的服務端,來接收客戶端的 ajax post 請求。

server.js

const Koa = require('koa')
const app = new Koa()
const Router = require('koa-router')
const router = new Router()
const bodyParser = require('koa-bodyparser')
const cors = require('koa-cors')

// 由於服務器與客戶端端口不同,需要 cors 中間件處理跨域請求
app.use(cors())
// 使用 bodyParser 中間件來解析 post 請求的請求體
app.use(bodyParser())
// 使用 koa-router 路由中間件來處理對應 URL 的請求事件  
app.use(router.routes())






router.post('/ajaxTest',async ctx => {    
    let order=ctx.request.body.order
    // 打印出客戶端傳遞的當前第n個請求的序號
    console.log(order)
    // 返回給客戶端的數據
    ctx.body = {success:true,data:'成功'}
});

app.listen(3000)

server.js 是koa服務端,由於客戶端端口號和服務端不同(客戶端80,服務端3000),所以需要做跨域處理,這裏爲了方便直接使用了 cors 插件,該服務器只接收一種請求,就是請求地址爲http://127.0.0.1:3000/ajaxTest,且請求類型爲 post 的 ajax 請求。

接下來是客戶端

client.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>按序串行發送100個ajax</title>
</head>
<body>
	<script>
		function myajax(url,data,method,success,async = true){
		    // 1. 創建對象
		    var xhr
		    if (window.ActiveXObject) {  
		    	xhr =  new ActiveXObject("Microsoft.XMLHTTP");  
		  	} else if (window.XMLHttpRequest) {  
		    	xhr = new XMLHttpRequest();  
		  	}
		    if(method == 'get'){
		        // 處理get請求,data 包含在 url 地址中
		        if(data){
		            url += '?'
		            url += data
		        }
		        // 2.設置方法和url
		        xhr.open(method,url,async)
		        // 3.發送請求
		    	xhr.send()
		    } else {
		        // 處理post請求
		        xhr.open(method,url,async)
		        // 2. 設置請求報文 指定編碼格式
		        xhr.setRequestHeader("Content-type","application/json");
		        // 3.發送請求
		        if(data){
		    		// data 在 send 中發送
		            xhr.send(data)
		        }else{
		            xhr.send()
		        }
		    }
		    
		    // 4.註冊事件
		    xhr.onreadystatechange = function(){
		        if(xhr.readyState == 4 && xhr.status == 200){
		            // 5.使用回調函數
		            success(xhr.responseText)
		        }
		    } 
		}
		myajax('http://127.0.0.1:3000/ajaxTest',JSON.stringify({order:1}),'post',(res)=>{
			var data = JSON.parse(res).data
			console.log(data)
		})

	</script>
</body>
</html>

這裏的 myajax 是封裝的原生的 ajax 函數,沒有用第三方插件的 Ajax 方法也是爲了方便對傳遞的參數的理解。

這裏先不急着發送100個ajax請求,先發送一個請求看看服務端能不能收到數據。

終端下運行命令 node server 啓動服務器

直接雙擊打開 client.html 客戶端,頁面加載完便會發送一個ajax請求 

服務器收到請求後在終端打印出一個1,說明請求發送成功。

1.1 嘗試使用for循環發送請求

首先通過默認的 myajax 函數,先嚐試向同一個地址發送 5 個請求。

for(let i = 1; i <= 5; i++){
	myajax('http://127.0.0.1:3000/ajaxTest',JSON.stringify({order:i}),'post',(res)=>{
		var data = JSON.parse(res).data
		console.log(data)
	})
}

由於請求發送的數據比較簡單,又是訪問的是同一個地址,大部分情況下序號的打印都是有序的,在大量數據的測試下,還是會出現發送的請求處理的順序發生錯位的情況,例如上圖中的一次請求打印的結果是 1,2,3,5,4  正常按序的打印的結果應該是1,2,3,4,5,這裏異步請求處理的順序就發生了錯位。 

然後我把服務端的輸出做了下微調

router.post('/ajaxTest',async (ctx,next) => {    
    let order=ctx.request.body.order
    console.log('當前執行的是:'+order)
    ctx.body = {success:true,data:'成功'}
    next()
});

同時把客戶端發送的ajax請求增加到100個

for(let i = 1; i <= 100; i++){
	myajax('http://127.0.0.1:3000/ajaxTest',JSON.stringify({order:i}),'post',(res)=>{
		var data = JSON.parse(res).data
		console.log(data)
	})
}

 

因爲ajax請求是異步的,這時候前10個ajax請求處理的順序錯位就比較厲害了,就無法達到按序處理的效果。但是10個以後的請求順序基本沒有錯亂。

* 有時候應用的場景要求是對100個不同的url地址發起請求,這裏爲了方便,只對一個 url 地址發起請求,實際上也是也一樣的,只不過寫100個 url 響應很麻煩而已。

1.2 第一種方案,設置 ajax 請求爲同步而不是異步

ajax請求默認是異步的,由於是異步的就無法保證ajax請求按序執行,這時候只要把 ajax 的 async 屬性設置成 false,即可同步等待逐個發送請求。

這裏試着把 ajax 的 async 屬性設置爲 false 變成同步處理,默認爲 true 爲異步。先發10個請求試試。

for(let i = 1; i <= 10; i++){
	myajax('http://127.0.0.1:3000/ajaxTest',JSON.stringify({order:i}),'post',(res)=>{
		var data = JSON.parse(res).data
		console.log(data)
	}, false)
}

反正至少在我測試的幾十次內都沒有發生錯位。理論上也不會發生錯位的情況,畢竟瀏覽器要等待ajax請求一個一個執行。

這種方法在少量 ajax 請求的時候可以,但是 ajax 請求一旦增多到 100,甚至 1000 個的時候,處理的時間增長,由於設置了同步,瀏覽器會等待 ajax 請求一個一個執行,頁面會發生假死的情況,不能操作頁面的任何內容,給用戶帶來的體驗很不好。

 

1.3 第二種方案,嘗試使用遞歸回調的方式發送請求

服務端代碼不變,客戶端用遞歸的方式來發送請求。其實就是在上一個ajax成功的回調裏面繼續調用一下ajax,然後使用一個公共的計數器來記錄當前所在的序號,當序號到達要請求的數組的末尾的時候,結束遞歸。

client.html中的代碼如下(封裝的myajax部分不變)

var data = []
for(let i = 1; i <= 100; i++){
	data.push(i)
}
var currentIndex = 0

function sendRequest(){
	if(currentIndex == data.length){
		// 遞歸結束的條件
		return;
	}
	var order = data[currentIndex]

	myajax('http://127.0.0.1:3000/ajaxTest',JSON.stringify({order:order}),'post',(res)=>{
		res = JSON.parse(res).data
		if(res == "成功"){
			currentIndex++
			sendRequest()
		} 
	})
}
sendRequest()

這裏也可以把 data 數組中的數據換成 url 地址,把固定的 url 地址部分換成變量,就變成了按序請求 100 個 url 地址。 

由於是前一個ajax發送成功後在回調裏面再繼續執行下一個請求,所以這裏一定是有序的。

1.4 第三種方案 async/await 方法

要使用 async 和 await 方法就要重新封裝 ajax 方法,因爲 await 後面是一個 Promise 對象,所以需要把 ajax 方法放到一個Promise對象中去返回。

function ajax(url, data){
	return new Promise((resolve,reject)=>{
		myajax(url,JSON.stringify({order:data}),'post',(res)=>{
			resolve(res)
		})

	}).then((res)=>{
		var data = JSON.parse(res).data
		console.log(data)
	})
}



async function sendRequest() {
	for(let i = 1; i <= 100; i++){
		await ajax('http://127.0.0.1:3000/ajaxTest',i)
	}
}

sendRequest()

async 函數返回一個 Promise 對象,當函數執行的時候,一旦遇到 await 就會先返回,等異步操作完成,再接着執行函數體內後面的語句。這也就保證了 ajax 請求能夠串行執行。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章