JS中按照順序依次執行多個異步任務


一、問題描述

有很多個請求需要依次發送,待上一個請求完成之后再發送下一個請求,發生異常時也要能夠繼續后面的請求。

二、思路

一個請求完成之后再發送下一個請求,關鍵在於發送一個之后先停下來等待該請求完成,處理之后再繼續下一個請求。生成器generator里面的yield語句可以分割代碼,程序遇到yield會停住,通過next語句可以一次執行一個yield分割的語句,本文嘗試使用生成器完成依次發送多個請求的功能。

生成器示例如下:

 1 // 生成器通過函數名前加*創建
 2 function * generatorFn() {
 3     yield 1;
 4     yield 2;
 5 }
 6 // 調用生成器
 7 let g = generatorFn();
 8 // 通過next方法執行,返回yield后面的值,且整個生成器未執行完之前done為false
 9 console.info(g.next()); // {value: 1, done: false}
10 console.info(g.next()); // {value: 2, done: false}
11 // 執行完之后value為undefined,done為true
12 console.info(g.next()); // {value: undefined, done: true}
13 console.info(g.next()); // {value: undefined, done: true}

三、步驟

通過依次發送100個不同的請求來介紹,首先先把100個請求放入生成器內:

1 let baseUrl = 'https://jsonplaceholder.typicode.com/todos/';
2 function* generator() {
3     for (let i = 1; i <= 100; i++) {
4         yield fetch(baseUrl + i);
5     }
6 }

接下來要先調用next()開始一個yield語句,讓請求發送出去,然后處理好結果后再次調用next(),如此循環,知道next()返回done: true,通過遞歸實現如下:

 1 function run(res) {
 2     if (!res.done) {
 3         res.value
 4             .then((response) => response.json())
 5             .then((json) => {
 6                 console.log(json);
 7                 document.writeln('id: ' + json.id + '<br>');
 8                 run(gen.next());
 9             });
10     }
11 }
12 
13 // 開始執行
14 let gen = generator();
15 run(gen.next());
可以觀察到頁面中會按照請求發送的順序輸出結果如下:

完整代碼如下:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3     <head>
 4         <meta charset="UTF-8" />
 5         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 6         <title>fetch one by one</title>
 7         <script>
 8             // 構造100個請求
 9             let baseUrl = 'https://jsonplaceholder.typicode.com/todos/';
10             function* generator() {
11                 for (let i = 1; i <= 100; i++) {
12                     yield fetch(baseUrl + i);
13                     if (i == 4) { // 故意制造一個異常
14                         yield fetch('fsfdsfsdf');
15                     } else {
16                         yield fetch(baseUrl + i);
17                     }
18                 }
19             }
20 
21 
22             // 處理請求結果並發送下一次請求
23             function run(res) {
24                 if (!res.done) {
25                     res.value
26                         .then((response) => response.json())
27                         .then((json) => {
28                             console.log(json);
29                             document.writeln('id: ' + json.id + '<br>');
30                             run(gen.next());
31                         })
32                         .catch((err) => { // 處理一下異常
33                             console.info(err);
34                             run(gen.next());
35                         });
36                 }
37             }
38             // 開始執行
39             let gen = generator();
40             run(gen.next());
41         </script>
42     </head>
43     <body></body>
44 </html>


或者封裝一下fetch,在請求成功之后調用生成器的next,執行下一個fetch,省去了調度的run函數:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body></body>
    <script>
        let baseUrl = 'https://jsonplaceholder.typicode.com/todos/';
        function* generator() {
            for (let i = 1; i <= 100; i++) {
                yield ajax(baseUrl + i);
            }
        }
        function ajax(url) {
            fetch(url)
                .then((res) => res.json())
                .then((res) => {
                    console.info(res.id);
                    gen.next();
                });
        }
        let gen = generator();
        gen.next();
    </script>
</html>

四、其他實現

1.通過promise的then實現。先得到一個成功的promsie,每次循環在promise.then里面返回一個新的promise,在新promise內發送請求,待請求成功后再resolve新promise,這樣下一次then的時候一定是上次resolve之后

function ajaxQuene() {
    let baseUrl = 'https://jsonplaceholder.typicode.com/photos/';
    let promise = Promise.resolve();
    for (let i = 1; i <= 100; i++) {
     // 關鍵就是要接收新返回的promise promise
= promise.then(() => { return new Promise((resolve, reject) => { fetch(baseUrl + i).then(res=>res.json()).then((res)=>{ console.info(res.id); resolve(); }) }) }) } } ajaxQuene();

下面的分步驟代碼會比較直接:

let promise = Promise.resolve();
promise = promise.then((v) => {
    return new Promise((resolve, reject) => {
        fetch('https://jsonplaceholder.typicode.com/photos/1').then((res) => {
            console.info(1);
            resolve(res);
        });
    });
});
// 這個then的執行必定是上個返回的promise在請求完成調用resolve之后 promise
= promise.then((v) => { return new Promise((resolve, reject) => { fetch('https://jsonplaceholder.typicode.com/photos/2').then((res) => { console.info(2); resolve(res); }); }); }); promise = promise.then((v) => { return new Promise((resolve, reject) => { fetch('https://jsonplaceholder.typicode.com/photos/3').then((res) => { console.info(3); resolve(res); }); }); });

 

2.await會強制其他代碼等待,直到后面的promise執行完畢,可以使用async和await完成類似功能:

 1 // 使用async和await
 2 let baseUrl = 'https://jsonplaceholder.typicode.com/todos/';
 3 async function ajax() {
 4     for (let i = 1; i <= 100; i++) {
 5         await fetch(baseUrl + i).then((response) => response.json())
 6             .then((json) => {
 7                 console.log(json);
 8                 document.writeln('id: ' + json.id + '<br>');
 9             })
10             .catch((err) => { // 處理一下異常
11                 console.info(err);
12             });
13     }
14 }
15 ajax();

以上項目中需要依次發送異步請求的實現方法,JS異步的發展就是讓異步處理起來越來越像同步。

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM