【JavaScript的ES6语法】10、generator实例-runner

上一篇学习了generator中yield关键字的原理和使用方式,本篇我们来编写一个generator的实例,该实例使用generator配合Promise将异步操作改造成同步操作。

一、先说效果

注:以下方法逻辑为“开课吧”老师石川(Blue)原创,这里仅进行剖析学习。
我们先创建1.txt、2.txt和3.txt三个文件,里面分别存储了数组和json:

我们要实现的效果是,使用ajax模拟网络请求,获取文本文件内容,并同时打印三个文本内容。要实现上面的效果,我们需要封装一个名为runner的方法,来将异步操作改造为同步操作。先不说方法怎么写,我们先来看结果,这里新建一个html,引入runner的js和jquery,像写同步方法一样,通过ajax异步获取三个文本的数据,最后一起打印:

<!DOCTYPE html>
<html>
<head>
    <title>TEST ES6</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- 引入jQuery -->
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    <script src="runner.js"></script>
</head>
 
<body>
    <script>
        runner(function *demo(){
            let data1=yield $.ajax({url: 'data/1.txt',dataType:'json'});
            let data2=yield $.ajax({url: 'data/2.txt',dataType:'json'});
            let data3=yield $.ajax({url: 'data/3.txt',dataType:'json'});

            console.log(data1,data2,data3);
        });
    </script>
</body>
</html>

按照之前的逻辑,在console打印的时候,三个data可能还在异步获取中,可能数据会缺失,但是使用generator配合Promise,可以实现就像同步语句一样,一步一步执行,上一步执行完毕后再执行下一步。
提示:Promise体现在哪?体现在ajax,因为ajax本身就是一个Promise对象。

那么这个runner方法怎么写,能实现这个效果?我们先来剖析一下整个方法的逻辑,首先runner方法输入的是一个名为demo的generator函数,在改函数中,获取第一个data1的时候,使用了yield关键字,yield的输入对象为一个ajax方法,即一个Promise对象,此时方法的操作权被暂停,直到获取到ajax的请求结果后,yield放行,获取到结果“data1”(下面的“data2”和”data3“以此类推)。
我们着手开始写。

二、开始写实例

我们新建一个名为runner的js文件,在该文件中我们封装一个名为runner的方法,写下相关逻辑:

function runner(_gen){//参数为一个generator函数
    //返回一个Promise函数,参数为一个包含成功和失败参数的方法
    return new Promise((resolve,reject)=>{
        var gen=_gen();  //获取传入的generator函数
        _next();//执行_next方法
        //嵌套一个_next方法,该方法是一个回调函数
        function _next(_last_res){
            //接受上一次的结果作为参数,执行下一步,并得到结果res
            var res=gen.next(_last_res);
            //判断done,done属性表示遍历是否结束,即这里是不是generator的yield分割的最后一步
            if(!res.done){//不是最后一步
                var obj=res.vlaue;//获取结果对象的值
                if(obj instanceof Promise){//如果结果对象是一个Promise对象
                    //结果是Promise对象,去执行,如果执行成功,就回调下一段generator的逻辑
                    obj.then((res)=>{
                        _next(res);
                    },(err)=>{//如果失败,就执行失败的方法
                        reject(err);
                    });
                }else if(typeof obj=='function'){//如果结果对象是一个方法
                    //如果该方法是一个generator函数,就执行runner进行嵌套
                    if(obj.constructor.toString().startWith('function GeneratorFunction()')){
                        runner(obj).then(res=>_next(res),reject);
                    }else{
                        _next(obj());//普通方法,就直接将方法返回给下一层
                    }
                }else{//如果结果对象既不是一个Promise对象,也不是一个方法
                    _next(obj);//此时把结果对象直接返回给下一层
                }
            }else{
                //如果是generator的yield分割的最后一步,直接执行该Promise对象自己的成功方法
                resolve(res.value);
            }
        }
    });
};

这里不在给大家赘述,每行的注释都写的很清楚,大家要自己敲一敲,细细领会里面的逻辑。

三、总结

总结一下,异步操作目前有以下几种写法:
1、回调:传统的ajax写法,在第一层成功获取数据后的方法里,调用下一层的方法,以此类推。就像(伪代码):

ajax('/banners',function success(banners_data){
    //导航栏数据处理
    ajax('/hotItems',function success(hotItems_data){
        //热门数据处理
        ajax('/slides',function success(slides_data){
            //滚动栏数据处理
        },function error(){
            alert('加载失败!');
        });
    },function error(){
        alert('加载失败!');
    });
},function error(){
    alert('加载失败!');
});

2、Promise:放一堆ajax异步方法进去,全部执行完进入成功方法,有一个失败就进入失败方法。就像:

Promise.all([
    $.ajax({ url: 'arr.txt', dataType: 'json' }),
    $.ajax({ url: 'json.txt', dataType: 'json' })
]).then(function (arr) {
    let [arr1, arr2] = arr;
    alert('都成功了!' + arr1 + JSON.stringify(arr2));
}, function (err) {
    console.log(err);
    alert('至少有一个失败了!');
});

Promise适合的场景是一次读一堆,不适合中间穿插逻辑的情况,具体原因看下面3中generator的对比。

3、generator:一个一个执行ajax异步,每一个ajax异步没有执行完之前,方法都是暂停的,直到执行完毕,放行给下一个跑。就像:

runner(function *demo(){
    let data1=yield $.ajax({url: 'xxx',dataType:'json'});
    //中间可以穿插逻辑代码
    let data2=yield $.ajax({url: 'yyy',dataType:'json'});
    //中间可以穿插逻辑代码
    let data3=yield $.ajax({url: 'zzz',dataType:'json'});

    console.log(data1,data2,data3);
});

这里要说的一点,generator在执行每个异步的时候,中间是可以穿插逻辑的,而Promise必须等所有异步执行完了,才能操作,所以Promise只适合不穿插任何逻辑的情况,generator适合中间穿插逻辑的情况。


参考:深入解读ES6系列视频教程(kaikeba.com提供,主讲老师石川(Blue))

转载请注明出处:https://blog.csdn.net/acmman/article/details/116566419

おすすめ

転載: blog.csdn.net/u013517797/article/details/116566419