2024-11-08
5 min read

Front-end notes

前端API特性或某些情境的測試與心得.

Timer

  1. [Mac-Chrome-v130] 切換到不同分頁或開啟其他應用程式不會讓setIntervalsetTimeout的時間延遲,但若將遊覽器縮小至底部, timer的時間將會逐漸被延遲至60s然後持續延遲60s(包含timer內部再執行的timer)
  2. [Mac-Safari-v17.4] 切換到不同分頁會讓setIntervalsetTimeout的時間逐漸延遲(不包含timer內部再執行的timer),且延遲時間不一定,實驗的延遲第一段約遞增至60s,第二段約80s,第三段約120s,但也可能低於60s或超過更多時間,取決於當時遊覽器是否忙碌(websocket不會斷線,仍可收發訊息)
  3. [Mac-Safari-v17.4] 開啟其他應用程式會讓setIntervalsetTimeout的時間被多延遲1s(包含timer內部再執行的timer)(websocket不會斷線,仍可收發訊息)

Websocket

  1. [Chrome]console network panel設置為offline時,websocket不會斷線,只是無法接收訊息,也暫時無法發送訊息至server端,panel上會顯示送出但實際並未送達server端,當把network狀態切回no throttling時,會將剛剛累積未送的訊息送給server端(待確定server端送出的訊息,遊覽器是否會補給client端,觀察後感覺遊覽器中間有一層queue幫忙紀錄)
  2. [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))

這時第一個requestIdleCallbackCallback 會等五秒後就執行且idleDeadline傳參的didTimeout欄位為true,因為已超過等候時間,所以就會強制執行callback方法.

第二個requestIdleCallbackCallback 完全不會執行,直到切回該分頁後才執行,且idleDeadline參數的didTimeout欄位為false,因為沒有逾時.