Front-end notes
前端API特性或某些情境的測試與心得.
Timer
- [Mac-Chrome-v130] 切換到不同分頁或開啟其他應用程式不會讓
setInterval或setTimeout的時間延遲,但若將遊覽器縮小至底部, timer的時間將會逐漸被延遲至60s然後持續延遲60s(包含timer內部再執行的timer) - [Mac-Safari-v17.4] 切換到不同分頁會讓
setInterval或setTimeout的時間逐漸延遲(不包含timer內部再執行的timer),且延遲時間不一定,實驗的延遲第一段約遞增至60s,第二段約80s,第三段約120s,但也可能低於60s或超過更多時間,取決於當時遊覽器是否忙碌(websocket不會斷線,仍可收發訊息) - [Mac-Safari-v17.4] 開啟其他應用程式會讓
setInterval或setTimeout的時間被多延遲1s(包含timer內部再執行的timer)(websocket不會斷線,仍可收發訊息)
Websocket
- [Chrome]console network panel設置為offline時,
websocket不會斷線,只是無法接收訊息,也暫時無法發送訊息至server端,panel上會顯示送出但實際並未送達server端,當把network狀態切回no throttling時,會將剛剛累積未送的訊息送給server端(待確定server端送出的訊息,遊覽器是否會補給client端,觀察後感覺遊覽器中間有一層queue幫忙紀錄) - [Chrome]當server發送ping frame時,遊覽器會自動返回pong frame給server端,並不會在console network的ws message中顯示任何ping/pong 資訊
websocket server 範例:
import Websocket, { WebSocketServer } from 'ws';
import http from 'node:http'
import fs from 'node:fs'
const httpPort = 9527
const port = 6633
const path = '/echo'
const indexHtml = fs.readFileSync('./index.html')
const httpServer = http.createServer((req,res) => {
console.log('request', req.url)
res.end(indexHtml)
})
const server = new WebSocketServer({ port, path, perMessageDeflate: false })
let msgCount = 0
const clients = new Map()
let count = 1;
server.on('connection', function (ws) {
console.log('connection in', count)
clients.set(ws, count++)
ws.on('error', function(e) {
console.log('[error] error', e)
});
ws.on('open', function open() {
console.log('[open] open event')
ws.send(Buffer.from('hello client'))
})
ws.on('message', function (data) {
const text = data.toString()
console.log('[message] received:', typeof data, data, text);
switch (text) {
case 'silent':
console.log('server not send any msg')
break;
case 'total':
ws.send(Buffer.from(`server clients count:${server.clients.size}`))
break;
default:
ws.send(`server send msg:${msgCount++}`)
break;
}
})
ws.on('close', function (code) {
const clientId = clients.get(this)
console.log(`[close] start remove client code:${code}, clientId: ${clientId}`)
if (clientId) {
clients.delete(this)
console.log(`[close] end remove clientId: ${clientId}, PingTimerId:${timerId}`)
clearInterval(timerId)
}
})
// 收到 ping 訊息事件
ws.on('ping', function () {
const clientId = clients.get(this)
console.log('server ws send ping', clientId)
})
// 收到 pong 訊息事件
ws.on('pong', function () {
const clientId = clients.get(this)
console.log(`clientId:${clientId} ws receive pong`)
})
// 每五秒發送一次 ping frame 到用戶端
const timerId = setInterval(() => {
ws.ping(900, false, (err) => {
if (err) {
console.log('[ping] failed', err)
}
})
}, 5000)
})
httpServer.listen(httpPort, () => {
console.log(`http server start listen port:${httpPort}`)
})
console.log(`ws server start listen path:${path}, port:${port}`)
requestIdleCallback
當遊覽器沒空時,不會執行requestIdleCallback裡面的callback方法,但如何測試?
先將遊覽器的控制台與頁面視窗分離,然後切換到另一個分頁,找個有input頁面的網站輸入文字,然後再去另一個分頁的控制台輸入以下script.
window.requestIdleCallback((idleDeadline) => console.log('idleDeadline', idleDeadline), { timeout: 5000 })
window.requestIdleCallback((idleDeadline) => console.log('idleDeadline', idleDeadline))
這時第一個requestIdleCallback的 Callback 會等五秒後就執行且idleDeadline傳參的didTimeout欄位為true,因為已超過等候時間,所以就會強制執行callback方法.
第二個requestIdleCallback的 Callback 完全不會執行,直到切回該分頁後才執行,且idleDeadline參數的didTimeout欄位為false,因為沒有逾時.