异步编程技术使你的程序可以在执行一个可能长期运行的任务的同时继续对其他事件做出反应而不必等待任务完成。与此同时,你的程序也将在任务完成后显示结果。
浏览器提供的许多功能(尤其是最有趣的那一部分)可能需要很长的时间来完成,因此需要异步完成,例如:
使用 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; response.writeHead(200, { 'Content-Type': 'text/plain', 'Access-Control-Allow-Credentials': true, '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' }); 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操作阻塞页面的渲染然,从而提高用户体验,所以说异步是前端所必须的,离开了异步事件处理模型,那么浏览器几乎成了没用的机器。。。因为有了异步,才让浏览器能够渲染如此复杂的页面。