串行按序發送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 請求能夠串行執行。