目录结构
├── server.js
├── index.html
分别保存如下代码到对应文件,运行
node server.js
html使用fetch作为请求库,请使用新版浏览器
浏览器打开http://127.0.0.1:3000点击对应按钮请求数据,观察页面响应状态和控制台输出,也可以用此来模拟并发场景下,阻塞和非阻塞对其他请求的影响.界面如图:
服务端建立了三个接口:
- 访问 /async 接口时执行非阻塞程序
- 访问 /sync 接口时执行阻塞程序
- 访问 /other 接口时执行没有延时的普通处理程序
请求普通接口时,程序会立即响应数据;
请求非阻塞接口时会请求返回promise的处理程序,将一个定时器放入事件队列,等10秒后定时器结束后将promise决议,程序响应http请求,如果在此期间请求普通接口,由于cpu并没有被阻塞住,所以可以很快响应数据;
请求阻塞接口时控制台开始疯狂打印日志,由于console.log是同步且cpu密集型任务,会将cpu一直占用,此时请求普通接口将一直被阻塞住,直到log结束后cpu才有空闲处理其他请求;
服务端代码server.js
const http = require('http');
const fs = require('fs');
const util = require('util');
const readFileAsync = util.promisify(fs.readFile)
// 启动一个web服务器
const server = http.createServer(async (req, res) => {
let data
res.writeHead(200, { 'Content-Type': 'text/plain' });
switch (req.url) {
case '/async':
await asyncOperation(10000)
data = "async"
break;
case '/sync':
await syncOperation(100000)
data = "sync"
break;
case '/other':
console.log('other');
data = "other"
break;
default:
res.writeHead(200, { 'Content-Type': 'text/html' });
data = await readFileAsync('./index.html')
break;
}
res.end(data);
});
server.listen(3000)
/**
* 异步返回
* @param int timeout
*/
function asyncOperation(timeout) {
let i = 0
let interval = setInterval(() => {
console.log(i++);
}, 1000);
return new Promise(resolve => {
setTimeout(() => {
clearInterval(interval)
resolve()
}, timeout);
})
}
/**
* console.log是同步的阻塞处理程序
* @param int times
*/
function syncOperation(times) {
for (let index = 0; index < times; index++) {
console.log(index)
}
}
页面代码index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vue@next"></script>
<title>同步异步</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
#app {
width: 100%;
min-height: 100%;
overflow-y: auto;
display: flex;
}
.box {
flex: 1;
text-align: center;
border-right: solid 1px #000;
}
.box:last-child {
border: none;
}
button {
padding: 5px 10px;
cursor: pointer;
}
.responseList {
margin-top: 5px;
border-top: 1px solid #000;
overflow-y: auto;
}
li {
list-style: none;
}
</style>
</head>
<body>
<div id="app">
<div class="box">
<h1>阻塞接口</h1>
<button @click='sendRequest("sync")'>发送请求</button>
<div class="responseList">
<li v-for="(item, i) in data.syncRspList" :key="i">
{{ data.syncRspList.length - i }} : {{ item }}
</li>
</div>
</div>
<div class="box">
<h1>非阻塞接口</h1>
<button @click='sendRequest("async")'>发送请求</button>
<div class="responseList">
<li v-for="(item, i) in data.asyncRspList" :key="i">
{{ data.asyncRspList.length - i }} : {{ item }}
</li>
</div>
</div>
<div class="box">
<h1>普通接口</h1>
<button @click='sendRequest("other")'>发送请求</button>
<div class="responseList">
<li v-for="(item, i) in data.otherRspList" :key="i">
{{ data.otherRspList.length - i }} : {{ item }}
</li>
</div>
</div>
</div>
<script>
let reqUrl = {
sync: {
listObj: "syncRspList",
url: "/sync"
},
async: {
listObj: "asyncRspList",
url: "/async"
},
other: {
listObj: "otherRspList",
url: "/other"
},
}
const vueConfig = {
setup() {
const data = Vue.reactive({
syncRspList: [],
asyncRspList: [],
otherRspList: [],
})
async function sendRequest(type) {
const { url, listObj } = reqUrl[type]
const arr = data[listObj]
let arrLength = arr.unshift("等待响应...")
data[listObj]
let res
try {
res = await fetch(url)
res = await res.text()
}
catch (e) {
res = "请求超时!"
}
finally {
arr.splice(-arrLength, 1, res)
}
}
return {
data,
sendRequest,
}
}
}
Vue.createApp(vueConfig).mount('#app')
</script>
</body>
</html>