js 轮询机制

概念

事件轮询 (eventloop) 是"一个解决和处理外部事件时将它们转换为回调函数的调用的实体(entity)"

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。所有任务都需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

任务队列

“ 任务队列 " 是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空," 任务队列 " 上第一位的事件就会自动进入主线程。

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务

异步任务指的是,不进入主线程、而进入 " 任务队列 "(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

JS如何实现异步操作

JS 的异步是通过回调函数实现的,即通过任务队列,在主线程执行完当前的任务栈(所有的同步操作),主线程空闲后轮询任务队列,并将任务队列中的任务(回调函数)取出来执行。

" 回调函数 "(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。

虽然 JS 是单线程的,但是浏览器的内核是多线程的,在浏览器的内核中不同的异步操作由不同的浏览器内核模块调度执行,异步操作会将相关回调添加到任务队列中。而不同的异步操作添加到任务队列的时机也不同,如 onclick, setTimeout,ajax 处理的方式都不同,这些异步操作是由浏览器内核的 webcore 来执行的,webcore 包含上图中的3种 webAPI,分别是 DOM Binding、network、timer模块。

onclick 由浏览器内核的 DOM Binding 模块来处理,当事件触发的时候,回调函数会立即添加到任务队列中。
setTimeout 会由浏览器内核的 timer 模块来进行延时处理,当时间到达的时候,才会将回调函数添加到任务队列中。
ajax 则会由浏览器内核的 network 模块来处理,在网络请求完成返回之后,才将回调函数添加到任务队列中。

异步执行机制

所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
主线程之外,还存在一个 " 任务队列 "(task queue)。只要异步任务有了运行结果,就在 " 任务队列 " 之中放置一个事件。
一旦 " 执行栈 " 中的所有同步任务执行完毕,系统就会读取 " 任务队列 ",看看里面有哪些事件。于是那些对应的异步任务结束等待状态,进入执行栈,开始执行。
主线程不断重复上面的第三步(事件轮询)

JS中事件队列的优先级

在 JS 的ES6 中新增的任务队列(promise)优先级是在事件循环之上的,事件循环每次 tick 后会查看 ES6 的任务队列中是否有任务要执行,也就是 ES6 的任务队列比事件循环中的任务(事件)队列优先级更高。

先执行同步代码再执行异步代码,先执行微任务再执行宏任务

setTimeout( fn, 0 ),它在 " 任务队列 " 的尾部添加一个事件,因此要等到同步任务和 " 任务队列 " 现有的事件都处理完,才会得到执行。

宏任务队列

宏任务:由宿主环境提供,比如setTimeout、setInterval、网络请求Ajax、用户I/O、script(整体代码)、UI rendering、setImmediate(node)

微任务队列

微任务:语言标准(ECMAScript)提供,如process.nextTick(node)、Promise、Object.observe、MutationObserver

要在服务端设置长轮询机制,可以使用以下步骤: 1. 接收客户端的长轮询请求。 2. 在服务端使用一个循环或定时器来等待新数据的到达。 3. 当有新数据可用时,将数据作为响应发送给客户端。 4. 如果在等待期间超时,没有新数据到达,可以选择返回一个空响应或者继续等待。 5. 客户端接收到响应后,处理数据,并重新发起新的长轮询请求。 具体实现方式会根据你使用的后端语言和框架而有所不同。下面是一个使用 Node.js 和 Express 框架实现长轮询的示例代码: ```javascript const express = require('express'); const app = express(); let data = null; app.get('/api/data', (req, res) => { const timeout = setTimeout(() => { if (data) { clearTimeout(timeout); res.json(data); } else { res.sendStatus(204); // 返回空响应,表示继续等待 } }, 5000); // 设置超时时间 // 可以在这里进行一些异步操作,比如查询数据库、请求外部接口等 }); app.post('/api/update', (req, res) => { // 接收到新数据时,更新数据并发送响应 data = req.body; res.sendStatus(200); }); app.listen(3000, () => { console.log('Server started on port 3000'); }); ``` 在上面的示例中,`/api/data` 路由用于接收长轮询请求,如果在等待期间超时且没有新数据到达时,将返回状态码 204 表示继续等待。`/api/update` 路由用于接收新数据,并将其保存在服务端,以便在长轮询请求时发送给客户端。 这只是一个简单的示例,实际情况可能需要根据具体需求对代码进行更多的处理和优化。另外,长轮询机制也可以通过使用 WebSocket 或其他推送技术来实现,这些方法可以实现实时的双向通信,而不需要轮询
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值