异步问题

异步编程技术使你的程序可以在执行一个可能长期运行的任务的同时继续对其他事件做出反应而不必等待任务完成。与此同时,你的程序也将在任务完成后显示结果。
浏览器提供的许多功能(尤其是最有趣的那一部分)可能需要很长的时间来完成,因此需要异步完成,例如:
使用 fetch() 发起 HTTP 请求
使用 getUserMedia() 访问用户的摄像头和麦克风
使用 showOpenFilePicker()请求用户选择文件以供访问
在这篇文章中,我们将从同步函数长时间运行时存在的问题开始,并以此进一步认识异步编程的必要性。

先看看前端处理一个比较耗时的任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<body>
<label for="quota">素数个数:</label>
<input type="text" id="quota" name="quota" value="1000000">
<button id="generate">生成素数</button>
<button id="reload">重载</button>
<div id="output"></div>
<script>
function isPrime(n) {
for (let c = 2; c <= Math.sqrt(n); ++c) {
if (n % c === 0) {
return false;
}
}
return true;
}
function generatePrimes(quota) {
const primes = [];
const maximum = 1000000;
while (primes.length < quota) {
const candidate = Math.floor(Math.random() * (maximum + 1));
if (isPrime(candidate)) {
primes.push(candidate);
}
}
return primes;
}
document.querySelector('#generate').addEventListener('click', () => {
const quota = document.querySelector('#quota').value;
const primes = generatePrimes(quota);
document.querySelector('#output').textContent = `完成!已生成素数${quota}个。`;
});
document.querySelector('#reload').addEventListener('click', () => {
document.location.reload()
});
</script>
</body>
阻塞
js代码执行会阻塞dom渲染和对dom的操作

再看看把比较耗时的任务交给后端,前端异步处理耗时任务的情况

前端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<body>
<label for="quota">素数个数:</label>
<input type="text" id="quota" name="quota" value="1000000">
<button id="generate">生成素数</button>
<button id="reload">重载</button>
<div id="output"></div>
<script>
document.querySelector('#generate').addEventListener('click', () => {
const quota = document.querySelector('#quota').value;
ajax.get("http://127.0.0.1:8081/test?quota=" + quota,(res)=>{
console.log(res)
document.querySelector('#output').textContent = `完成!已生成素数${quota}个。`;
})

});
document.querySelector('#reload').addEventListener('click', () => {
document.location.reload()
});
const ajax = {
get:function(url, cb){
var xml = new XMLHttpRequest();
xml.open("get", url, true);
xml.onreadystatechange = function(){
if(xml.status === 200 && xml.readyState ===4){
cb(xml.responseText)
}
}
xml.send();
}
}
</script>
</body>

后端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
var http = require('http');
var url = require("url");
http.createServer(function (request, response) {
let queryParse = url.parse(request.url, true);
console.log(queryParse)
const {pathname, parse, query} = queryParse;
// 设置cors一些列响应头来解决前端浏览器跨域问题
response.writeHead(200, {
'Content-Type': 'text/plain',
'Access-Control-Allow-Credentials': true,//允许后端发送cookie
'Access-Control-Allow-Origin': request.headers.origin || "*",//任意域名都可以访问,或者基于我请求头里面的域
'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',//设置请求头格式和类型
'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS',//允许支持的请求方式
'Content-Type': 'application/json; charset=utf-8'//默认与允许的文本格式json和编码格式
});
if(pathname === '/test'){
const res = generatePrimes(query.quota);
console.log("计算完成!")
return response.end(JSON.stringify(res));
}

response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World');

}).listen(8081);

console.log('Server running at http://127.0.0.1:8081/');
function generatePrimes(quota) {
function isPrime(n) {
for (let c = 2; c <= Math.sqrt(n); ++c) {
if (n % c === 0) {
return false;
}
}
return true;
}
const primes = [];
const maximum = 1000000;
while (primes.length < quota) {
const candidate = Math.floor(Math.random() * (maximum + 1));
if (isPrime(candidate)) {
primes.push(candidate);
}
}
return primes;
}
非阻塞
将耗时操作交给后端,前端通过异步回调的方式,来避免dom渲染被阻塞
结果对比
前端尽可能避免执行耗时的js操作,可以通过ajax等异步回调的手段来处理耗时任务

总结

我们在添加了事件监听器后发送请求。在这之后,我们仍然可以操作dom元素,也就是说,我们的程序可以在请求进行的同时继续运行,而我们的事件处理程序将在请求完成时被调用。这样通过异步回调方式就可以避免了前端因为耗时的js操作阻塞页面的渲染然,从而提高用户体验,所以说异步是前端所必须的,离开了异步事件处理模型,那么浏览器几乎成了没用的机器。。。因为有了异步,才让浏览器能够渲染如此复杂的页面。


异步问题
https://zbdev.online/2023/02/28/异步问题/
作者
zzb
发布于
2023年2月28日
许可协议