大家好,欢迎来到IT知识分享网。
今天来整理下之前在公司分享过的关于Javascript 同步/异步请求、响应的4个发展阶段;
如上图所示,主要分为4个阶段:
阶段一:同步时代->同步请求响应阶段,用户和网页交互性差、体验不好,页面执行一个耗时的网络操作就会卡住,用户必须等待操作结束才能继续和网页进行交互。如下代码所示:
<body>
<label for="quota">Number of primes:</label>
<input type="text" id="quota" name="quota" value="1000000">
<button id="generate">Generate primes</button>
<button id="reload">Reload</button>
<div id="output"></div><br/><br/>
<textarea style="width: 1000px;height: 200px;">type anything</textarea>
<script type="text/javascript">
//fake a long run http request operation
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;
}
document.querySelector('#generate').addEventListener('click', () => {
const quota = document.querySelector('#quota').value;
const primes = generatePrimes(quota);
document.querySelector('#output').textContent = `Finished generating ${quota} primes!`;
});
document.querySelector('#reload').addEventListener('click', () => {
document.location.reload()
});
</script>
</body>
IT知识分享网
这段代码的执行效果如下:
界面上用户可以交互的内容包括了输入框和按钮,Generate primes这个按钮会触发一个耗时的计算操作,一旦点击后,就会进入计算,而这时候会发现界面的其他按钮和输入框无法进行交互了,页面瞬间卡住了,用户体验差。
阶段二:AJAX时代 -> 异步请求响应阶段,可以做到局部刷新,用户不必等待请求返回,可以继续与页面上的其他组件进行交互。如下代码所示:
IT知识分享网<body>
<button id="xhr">Click to start request</button>
<button id="reload">Reload</button>
<pre readonly class="event-log"></pre>
<script type="text/javascript">
const log = document.querySelector('.event-log');
//AJAX: Asynchronized JAVAScript and XML
function xmlOpe(){
const xhr = new XMLHttpRequest();
xhr.addEventListener('loadend', () => {
log.textContent = `${log.textContent}Finished with status: ${xhr.status}`;
});
xhr.open('GET', 'https://www.google.com');
xhr.send();
}
document.querySelector('#xhr').addEventListener('click', () => {
log.textContent = '';
xmlOpe();
log.textContent = `${log.textContent}Started XHR request\n`;
});
document.querySelector('#reload').addEventListener('click', () => {
log.textContent = '';
document.location.reload();
});
</script>
</body>
这段代码的执行效果如下:
点击Click to start request按钮后,就会发出请求,而在发出请求后,用户可以继续在右边的输入框内输入操作,不必等待请求执行完成,体验良好;
在等待一段时间后,请求返回响应如下:
这种异步的效果会让用户感觉界面比较流畅,没有卡顿;
阶段三:Promise时代 -> 更加优雅的异步编程方式;
上面阶段二中用AJAX技术解决了用户与网页交互卡顿、不流畅等体验差的问题,但是后来使用的过程中发现,因为AJAX技术的回调机制会产生callback hell问题(回调函数的套壳),进而导致代码可读性低,不易维护等诸多问题,如下有一段callback hell的代码:
function xmlOpe2(){
const xhr = new XMLHttpRequest();
xhr.addEventListener('loadend', () => {
log.textContent = `${log.textContent}Finished with status: ${xhr.status}`;
});
xhr.open('GET', 'https://raw.githubusercontent.com/mdn/content/main/files/en-us/_wikihistory.json');
xhr.send();
}
function xmlOpe1(){
const xhr = new XMLHttpRequest();
xhr.addEventListener('loadend', () => {
xmlOpe2();
log.textContent = `${log.textContent}Finished with status: ${xhr.status}`;
});
xhr.open('GET', 'https://raw.githubusercontent.com/mdn/content/main/files/en-us/_wikihistory.json');
xhr.send();
}
//AJAX: Asynchronized JAVAScript and XML
function xmlOpe(){
const xhr = new XMLHttpRequest();
xhr.addEventListener('loadend', () => {
xmlOpe1();
log.textContent = `${log.textContent}Finished with status: ${xhr.status}`;
});
xhr.open('GET', 'https://raw.githubusercontent.com/mdn/content/main/files/en-us/_wikihistory.json');
xhr.send();
}
document.querySelector('#xhr').addEventListener('click', () => {
log.textContent = '';
xmlOpe();
log.textContent = `${log.textContent}Started XHR request\n`;
});
document.querySelector('#reload').addEventListener('click', () => {
log.textContent = '';
document.location.reload();
});
这段代码,就是在一段套壳代码的实现,xmlOpe->xmlOpe1->xmlOpe2… ,代码看上去比较复杂,如果一旦网页中出现较多的请求的异步加载操作,代码的结构将会极其复杂;所以后来有了Promise这种相对优雅的改进方式:
单个请求:
IT知识分享网const fetchPromise = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');
console.log(fetchPromise);
fetchPromise.then( response => {
console.log(`Received response: ${response.status}`);
});
console.log("Started request...");
请求的链式调用:
const fetchPromise = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');
console.log(fetchPromise);
fetchPromise
.then( response => {
// throw new Error(`HTTP error: ${response.status}`);
console.log(`Received response: ${response.status}`);
return response.json();
})
.then( json => {
// throw new Error(`json error parsed`);
console.log(json[0].name);
})
.catch( error => {
console.error(`Could not get products: ${error}`);
});
console.log("Started request...");
多个请求合并:
const fetchPromise1 = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');
const fetchPromise2 = fetch('invalide-schema://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');
const fetchPromise3 = fetch('invalide-schema://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json');
Promise.all([fetchPromise1, fetchPromise2, fetchPromise3])
.then( responses => {
for (const response of responses) {
console.log(`${response.url}: ${response.status}`);
}
})
.catch( error => {
console.error(`Failed to fetch: ${error}`)
});
会发现相比之前AJAX的代码,看起来要舒服很多,易于理解。
阶段四:Async/Await时代 -> 更加人性化的、符合直觉的异步编程方式,化异步为同步写法;
经过阶段三的演化,会发现Javascript中的异步操作其实已经变得优雅、可维护,但是这不是尽头,出现了一种更加符合直觉的写法,为什么说符合直觉呢?程序员编程往往大多数都会习惯代码一行行执行,而异步执行的代码往往是违反这种直觉的,就好比下面这份代码:
1: const fetchPromise1 = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');
2: func1(fetchPromise1);
3: func2();
4: console.log("end");
这份代码,第一感觉看起来,就是一句句执行,可是结果不是如此,在执行完第1句fetch操作后,fetch的操作是一个网络请求,这个网络请求其实并没有完成,如果这时候当你把参数fetchPromise1传入第2句func1中时,这是好的fetchPromise1是一个半完成状态的返回值,而不是最终结果,所以这就是为什么fetch操作一般都会配上then方法去处理请求结果,如下所示:
fetchPromise
.then( response => {
console.log(`Received response: ${response.status}`);
return response.json();
})
这里的then方法其实就是对请求完成后一个完整结果的处理,那么为了避免这种异步编程方式给程序员带来的困扰以及debug时候的疑惑等问题,就产生了下面的编程方式:
async function fetchProducts() {
try {
// after this line, our function will wait for the `fetch()` call to be settled
// the `fetch()` call will either return a Response or throw an error
const response = await fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
// after this line, our function will wait for the `response.json()` call to be settled
// the `response.json()` call will either return the JSON object or throw an error
const json = await response.json();
console.log(json[0].name);
}
catch(error) {
console.error(`Could not get products: ${error}`);
}
}
fetchProducts();
console.log("request start");
async/await这种编程模式就是化异步编程方式为同步编程方式,请求还是异步执行,而只是利于开发者理解,给开发者一种仿佛在写同步代码的感觉,实质上JS还是异步执行,对于程序员开发和实际程序运行可谓双赢。
async/await这对关键字是成对出现的,这也是Javascript对底层做了处理,以语法糖技术包装了一层,开发者才能有此收益。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/6950.html