JavaScript的ES6语法10generator实例-runner
Posted 光仔December
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript的ES6语法10generator实例-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
以上是关于JavaScript的ES6语法10generator实例-runner的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript的ES6语法9generator函数之yield关键字
JavaScript中ES6新特性-Generator异步方案