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异步的发展就是让异步处理起来越来越像同步。

 
 
posted on 2021-02-22 13:24  橘生淮南_  阅读(2554)  评论(0编辑  收藏  举报