十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
前端如何實(shí)時(shí)獲得后端不斷更新的數(shù)據(jù)?最容易實(shí)現(xiàn)的短輪詢有如下優(yōu)缺點(diǎn)。優(yōu)點(diǎn):開發(fā)簡單。缺點(diǎn):無用請求過多并卻不能保證數(shù)據(jù)的實(shí)時(shí)性。
成都創(chuàng)新互聯(lián)公司主營惠濟(jì)網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,成都App定制開發(fā),惠濟(jì)h5小程序制作搭建,惠濟(jì)網(wǎng)站營銷推廣歡迎惠濟(jì)等地區(qū)企業(yè)咨詢
如果對于數(shù)據(jù)要求較高,這個(gè)時(shí)候短輪詢就可以pass了。
下面我來介紹2種稍微高大上一點(diǎn)的方法,哈哈哈哈
一. Long Polling長輪詢解決方案什么是長輪詢?客戶端發(fā)起請求后,服務(wù)端發(fā)現(xiàn)當(dāng)前沒有新的數(shù)據(jù),這個(gè)時(shí)候服務(wù)端沒有立即返回請求,而是將請求掛起,在等待一段時(shí)間后(一般為30s或者是60s),發(fā)現(xiàn)還是沒有數(shù)據(jù)更新的話,就返回一個(gè)空結(jié)果給客戶端。客戶端在收到服務(wù)端的回復(fù)后,立即再次向服務(wù)端發(fā)送新的請求。這次服務(wù)端在接收到客戶端的請求后,同樣等待了一段時(shí)間,這次好運(yùn)的是服務(wù)端的數(shù)據(jù)發(fā)生了更新,服務(wù)端給客戶端返回了最新的數(shù)據(jù)??蛻舳嗽谀玫浇Y(jié)果后再次發(fā)送下一個(gè)請求,如此反復(fù)。
實(shí)現(xiàn)過程如下
//polling.js==>使用node polling.js啟動(dòng)服務(wù)器
const http = require('http')
const url = require('url')
const events = []//后端不斷變化的數(shù)據(jù)
let timers = new Set()
let subscribers = new Set()// 當(dāng)前掛起的請求
const EventProducer = () =>{
const event = {
id: Date.now(),
timestamp: Date.now()
}
events.push(event)
// 數(shù)據(jù)發(fā)生變化,通知所有掛起的請求,將變化的最新數(shù)據(jù)返回
subscribers.forEach(subscriber =>{
subscriber.resp.write(JSON.stringify(events.filter(event =>event.timestamp >subscriber.timestamp)))
subscriber.resp.end()
})
// 此時(shí)所有掛起的請求已經(jīng)處理完畢,清空subscribers
subscribers.clear()
// 取消請求的超時(shí)回調(diào)
timers.forEach(timer =>clearTimeout(timer))
timers.clear() // 重置timers
}
// 10秒生成一個(gè)事件
setInterval(() =>{
EventProducer()
}, 10000)
const server = http.createServer((req, resp) =>{
const urlParsed = url.parse(req.url, true)
resp.setHeader('Access-Control-Allow-Origin', '*')
resp.setHeader('Origin', '*')
if (urlParsed.pathname == '/list') {
// 發(fā)送服務(wù)端現(xiàn)存事件
resp.write(JSON.stringify(events))
resp.end()
} else if (urlParsed.pathname == '/subscribe') {
const timestamp = parseInt(urlParsed.query['timestamp'])
const subscriber = {
timestamp,
resp
}
// 新建的連接掛起來
subscribers.add(subscriber)
// 30s超時(shí),自動(dòng)關(guān)閉連接
const timer = setTimeout(() =>{
resp.end()
timers.delete(timer)
}, 30000)
// 客戶端主動(dòng)斷開連接
req.on('close', () =>{
subscribers.delete(subscriber)
clearTimeout(timer)
})
timers.add(timer)
}
})
server.listen(8090, () =>{
console.log('server is up')
})const fetchLatestEvents = async (timestamp) =>{
const body = await fetch(`http://localhost:8090/subscribe?timestamp=${timestamp}`)
if (body.ok) {
const json = await body.json()
return json
} else {
console.error('failed to fetch')
}
}
const listEvents = async () =>{
const body = await fetch(`http://localhost:8090/list`)
if (body.ok) {
const json = await body.json()
return json
} else {
console.error('failed to fetch')
}
}
var timestampRef = { timestamp: 0 }
var eventsRef = []
const fetchTask = async () =>{
if (timestampRef.timestamp === 0) {
// 初次加載
const currentEvents = await listEvents()
timestampRef.timestamp = currentEvents[currentEvents.length - 1].timestamp
eventsRef = [...eventsRef, ...currentEvents]
}
//注意latestEvents數(shù)據(jù)是后端新生成的數(shù)據(jù),前端需要自己拼上老數(shù)據(jù)
const latestEvents = await fetchLatestEvents(timestampRef.timestamp)
if (latestEvents && latestEvents.length) {
timestampRef = latestEvents[latestEvents.length - 1]
eventsRef = [...eventsRef, ...latestEvents]
}
}
function start() {
fetchTask()
.catch(err =>{
console.error(err)
}).finally(() =>{
// 觸發(fā)下次加載
start()
})
}
start()值得注意的是,這個(gè)時(shí)候,我們打開瀏覽器的調(diào)試工具可以發(fā)現(xiàn)瀏覽器每一次發(fā)出的請求都不會立馬收到回復(fù),而是pending一段時(shí)間后(大概是10秒)才會有結(jié)果,并且結(jié)果里面都是有數(shù)據(jù)的
二. WebSocket解決方案長輪詢優(yōu)缺點(diǎn) 優(yōu)點(diǎn):避免了客戶端大量的重復(fù)請求。再者客戶端在收到服務(wù)端的返回后,馬上發(fā)送下一個(gè)請求,這就保證了更好的數(shù)據(jù)實(shí)時(shí)性。
缺點(diǎn):服務(wù)端資源大量消耗,服務(wù)端會一直hold住客戶端的請求,這部分請求會占用服務(wù)器的資源。難以處理數(shù)據(jù)更新頻繁的情況:
如果數(shù)據(jù)更新頻繁,會有大量的連接創(chuàng)建和重建過程,這部分消耗很大。
什么是WebSocket? 客戶端和服務(wù)器之間建立一個(gè)持久的長連接,這個(gè)連接是雙工的,客戶端和服務(wù)端都可以實(shí)時(shí)地給對方發(fā)送消息。
實(shí)現(xiàn)過程如下
// websocket.js
const WebSocket = require('ws')
const events = []
let latestTimestamp = Date.now()
const clients = new Set()//連接著的socket數(shù)組
const EventProducer = () =>{
const event = {
id: Date.now(),
timestamp: Date.now()
}
events.push(event)
latestTimestamp = event.timestamp
// 推送給所有連接著的socket
clients.forEach(client =>{
client.ws.send(JSON.stringify(events.filter(event =>event.timestamp >client.timestamp)))
client.timestamp = latestTimestamp
})
}
// 每10秒生成一個(gè)新的事件
setInterval(() =>{
EventProducer()
}, 10000)
// 啟動(dòng)socket服務(wù)器
const wss = new WebSocket.Server({ port: 8080 })
wss.on('connection', (ws, req) =>{
console.log('client connected')
// 首次連接,推送現(xiàn)存事件
ws.send(JSON.stringify(events))
const client = {
timestamp: latestTimestamp,
ws,
}
clients.add(client)//將連接放入數(shù)組
ws.on('close', _ =>{
clients.delete(client)
})
})var timestampRef={current:0}
var eventsRef={current:[]}
const ws = new WebSocket(`ws://localhost:8080/ws?timestamp=${timestampRef.current}`)
ws.addEventListener('open', e =>{
console.log('successfully connected')
})
ws.addEventListener('close', e =>{
console.log('socket close')
})
ws.addEventListener('message', (ev) =>{
const latestEvents = JSON.parse(ev.data)
if (latestEvents && latestEvents.length) {
timestampRef.current = latestEvents[latestEvents.length - 1].timestamp
//注意latestEvents數(shù)據(jù)是后端新生成的數(shù)據(jù),前端需要自己拼上老數(shù)據(jù)
eventsRef.current = [...eventsRef.current, ...latestEvents]
console.log(eventsRef)
}
})你會發(fā)現(xiàn)客戶端和服務(wù)端只有一個(gè)websocket連接,它們所有的通信都是發(fā)生在這個(gè)連接上面的

WebSocket優(yōu)缺點(diǎn)
優(yōu)點(diǎn):客戶端和服務(wù)端建立連接的次數(shù)少。消息實(shí)時(shí)性高。雙工通信,服務(wù)端和客戶端都可以隨時(shí)給對方發(fā)送消息。相對于長輪詢適用于服務(wù)端數(shù)據(jù)頻繁更新的場景,因?yàn)榭蛻舳嗽谀玫叫畔⒑蟛恍枰匦陆⑦B接或者發(fā)送請求。
缺點(diǎn):系統(tǒng)設(shè)計(jì)較復(fù)雜。某些代理層軟件(如Nginx)默認(rèn)配置的長連接時(shí)間是有限制的,這個(gè)時(shí)候客戶端需要自動(dòng)重連,實(shí)現(xiàn)也會比較困難。
最后簡單介紹一下另外一種方案:Server-Sent Events
什么是SSE?客戶端向服務(wù)端發(fā)起一個(gè)持久化的HTTP連接,服務(wù)端接收到請求后,會掛起客戶端的請求,有新消息時(shí),再通過這個(gè)連接將數(shù)據(jù)推送給客戶端。這里需要指出的是和WebSocket長連接不同,SSE的連接是單向的,也就是說它不允許客戶端向服務(wù)端發(fā)送消息。
優(yōu)點(diǎn):和webSocket類似,連接數(shù)少。數(shù)據(jù)實(shí)時(shí)性高。缺點(diǎn):SSE長連接是單向的,不允許客戶端給服務(wù)端推送數(shù)據(jù)。和WebSocket一樣會遇到代理層配置的問題
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧