Javascript 同步/异步请求发展史[通俗易懂]

Javascript 同步/异步请求发展史[通俗易懂]今天来整理下之前在公司分享过的关于Javascript 同步/异步请求、响应的4个发展阶段;如上图所示,主要分为4个阶段:阶段一:同步时代-&g

大家好,欢迎来到IT知识分享网。

今天来整理下之前在公司分享过的关于Javascript 同步/异步请求、响应的4个发展阶段;

Javascript 同步/异步请求发展史[通俗易懂]

如上图所示,主要分为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知识分享网

这段代码的执行效果如下:

Javascript 同步/异步请求发展史[通俗易懂]

界面上用户可以交互的内容包括了输入框和按钮,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>

这段代码的执行效果如下:

Javascript 同步/异步请求发展史[通俗易懂]

点击Click to start request按钮后,就会发出请求,而在发出请求后,用户可以继续在右边的输入框内输入操作,不必等待请求执行完成,体验良好;
在等待一段时间后,请求返回响应如下:

Javascript 同步/异步请求发展史[通俗易懂]

这种异步的效果会让用户感觉界面比较流畅,没有卡顿;

阶段三: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");

这份代码,第一感觉看起来,就是一句句执行,可是结果不是如此,在执行完第1fetch操作后,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

(0)
上一篇 2023-01-03 09:54
下一篇 2023-01-03 09:54

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信