第八周第二天笔记
2018-09-08 本文已影响0人
果木山
ES6之Promise类
1 Promise类基础知识解读
- promise类的静态属性方法
- 分类:resolve(),reject(),all(),race()
- resolve():作为形参传入到实例对象中,在条件满足时,作为回调函数执行,可以传入实参;then中的第一个参数回调函数接收;
- reject():作为形参传入到实例对象中,在条件不满足时,即失败时,作为回调函数执行,可以传入实参,then中第二个参数回调函数接收,或是catch中的回调函数接收;
- all():提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调,即当最慢的一个没有执行完时,会执行完的异步操作传入的数据,以数组的形式储存,待最慢的一个执行完,以一个数组的形式,将数据传入到then;
- race():多个异步操作同样是并行执行的,谁最快,数据进入then;其他的异步操作会继续执行,但是then不会再接受其他异步操作的数据;
- 使用:Promise类名调用,即
Promise.all
;实例对象不能使用;
- 分类:resolve(),reject(),all(),race()
- promise类原型上的属性方法
- 分类:then(),catch()
- then():接收第一个参数,是回调函数,并且会拿到异步操作成功后resolve传入的参数,第二个参数可有可无,如果添加,也是一个回调函数,里面拿到的是,异步操作失败后reject传入的参数;
- catch():接受一个参数,是回调函数,第一个功能,是当异步操作失败后,接收reject传入的参数;第二个功能,是异步操作成功后,会进入then,但是在then中执行代码,报错了,然后,catch就接收到了报错的文本,然后不会终止JS,可以保证JS继续执行;
- 使用:实例对象才能使用原型的属性方法;
- 分类:then(),catch()
- 总结:实例创建中,当满足条件后,会执行resolve(),相当于then里面匿名函数调用;相当于回调函数,而resolve可以看做then里面匿名函数的函数名;异步操作其实就是实例对象自执行;
- Promise详细解读文件
2 Promise类实例
- 实例1:图片请求超时
- 知识点:将两个异步操作放入到race方法中,如果5秒之内图片请求成功了,那么便进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么imgTimeout最快执行,则进入catch,报出“图片请求超时”的信息。当requestImg函数中图片地址出错,图片无法正常加载,onload事件不会执行,resolve不会执行,所以不会进入then;
<script> function requestImg() { return new Promise(function (resolve) { var oImg=new Image(); oImg.onload=function(){ resolve(this); }; oImg.src="image1/01.png"; }) } function imgTimeout() { return new Promise(function (resolve, reject) { setTimeout(function () { reject("图片加载超时"); },5000) }) } Promise.race([requestImg(),imgTimeout()]) .then(function (data) { document.body.appendChild(data); }) .catch(function (reason) { console.log(reason); }); </script>
- 实例2:小球运动实例
- 知识点:多次执行异步操作,执行异步操作1后,当条件满足时,会执行异步操作1中resolve(),而在异步操作1点then的匿名函数中返回另一个异步操作2;然后执行异步操作2,实质为实例对象的自执行,然后满足条件时,执行异步操作2中的resolve(),然后再在异步操作2点then的匿名函数中返回异步操作3,反复异步操作,直到最后一个异步操作中条件满足后,执行resolve(),在其后面的then匿名函数中,添加运动结束代码;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>小球运动实例</title> <style> div{ width: 60px; height: 60px; border-radius: 50%; margin-bottom: 10px; } .div1{ background-color: red; } .div2{ background-color: green; } .div3{ background-color: yellow; } </style> </head> <body> <div class="div1" style="margin-left:0"></div> <div class="div2" style="margin-left:0"></div> <div class="div3" style="margin-left:0"></div> <script> var oDiv1=document.getElementsByTagName("div")[0]; var oDiv2=document.getElementsByTagName("div")[1]; var oDiv3=document.getElementsByTagName("div")[2]; //ele:操作的元素;target:元素运动到的目的地 function promiseAnimate(ele,target) {//函数返回值为实例对象 //resolve:成功之后想做什么,reject:失败之后想做什么 return new Promise(function(resolve,reject){ function animate() {//去掉参数 setTimeout(function () { var n=parseFloat(ele.style.marginLeft); if(n===target){ resolve();//做回调函数要做的事情 }else{ if(n<target){ n++; ele.style.marginLeft=n+"px"; }else{ n--; ele.style.marginLeft=n+"px"; } animate(); } },10); } animate();//调用一次; }) } promiseAnimate(oDiv1,100) .then(function () { return promiseAnimate(oDiv2,200) }) .then(function () { return promiseAnimate(oDiv3,300) }) .then(function () { return promiseAnimate(oDiv3,150) }) .then(function () { return promiseAnimate(oDiv2,150) }) .then(function () { return promiseAnimate(oDiv1,150) }) .then(function () { alert("运动结束") }) </script> </body> </html>
运动库
- 实例1:左右按钮点击运动
- 知识点:
- 边界值的判断:1)在cur累加累减之前,必须加减步长进行判断;2)在cur累加累减之后,不用加减步长进行判断,直接判断cur是否满足条件;
- 边界值判断条件成立,执行的语句中,设置边界值,然后添加return,阻断程序执行;
- 需注意:设置样式值,必须在边界值判断之后;;
<script> //1 边界值判断,在cur累加累减之前,必须加减步长进行判断; if(cur>target){ if(cur-5<=target){//提前加减步长进行比较,如果满足条件就设置目标值; oDiv.style.left=target+"px"; return; } cur-=5; }else{ if(cur+5>=target){ oDiv.style.left=target+"px"; return; } cur+=5; } oDiv.style.left=cur+"px"; //2 边界值判断,在cur累加累减之后,不用加减步长进行判断,直接判断cur是否满足条件,但是需注意的一点是,设置必须在判断条件之后; if(cur>target){ cur-=5; if(cur<=target){//在累加之后判断无需加减步长,如果满足条件就设置目标值; oDiv.style.left=target+"px"; return; } }else{ cur+=5; if(cur>=target){ oDiv.style.left=target+"px"; return; } } oDiv.style.left=cur+"px"; </script>
- 定时器的timer,设置在元素的自定义属性上,避免全局变量
- 两个点击事件执行一个定时器时会出问题,所以需要在执行定时器之前,关闭定时器;
- 优化思想:
- 出现的问题:定时器中要添加函数的定义阶段,不加括号,但是针对带参数的函数,就无法只写函数名,可以新建一个匿名函数将函数调用放入匿名函数中;如:
oDiv.timer=setTimeout(function(){ moveBat(target);},30)
,但是这样设置会出现问题,当定时器中的moveBat(target)执行值,会查找参数target,向匿名函数查找,查找不到再向上级作用域查找,而此时定时器中的匿名函数形成的私有作用域不会被释放,每执行一次定时器,都会新建一个私有作用域,所以内存会很大,不利于优化; - 解决措施:在moveBat(target)函数定义中,添加一个新的函数_move(),将所有代码放入其中,在定时器中添加_move,这样当定时器执行时,就会重复调用_move函数;不会再形成匿名函数;但是需注意的是,在添加_move定义阶段后,必须执行一次;
- 出现的问题:定时器中要添加函数的定义阶段,不加括号,但是针对带参数的函数,就无法只写函数名,可以新建一个匿名函数将函数调用放入匿名函数中;如:
- 代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>左右按钮点击运动</title> <style> *{ margin: 0; padding: 0; } button{ margin: 5px; width: 100px; height: 30px; line-height: 30px; cursor: pointer; } #but{ width:230px; height: 50px; margin: 0 auto; } #div1{ width: 100px; height: 100px; line-height: 100px; text-align: center; font-size: 30px; color: blue; border-radius: 50%; background-color: red; position: absolute; left: 500px; top: 100px; } .div2{ width: 10px; height: 500px; position: absolute; top: 50px; background-color: blue; } #div2{ left: 240px; } #div3{ left: 1250px; } </style> </head> <body> <div id="but"><button>向左</button><button>向右</button></div> <div id="div1">太阳</div> <div class="div2" id="div2"></div> <div class="div2" id="div3"></div> <script> var oDiv=document.getElementById("div1"); var aBtn=document.getElementsByTagName("button"); aBtn[0].onclick=function () { moveBat(250); }; aBtn[1].onclick=function () { moveBat(1150); }; function moveBat(target) { _moveBat();//执行一次; function _moveBat(){//添加函数,利于优化 var cur=oDiv.offsetLeft; if(cur>target){ if(cur-5<=target){//提前加减步长进行比较,如果满足条件就设置目标值; oDiv.style.left=target+"px"; return; } cur-=5; }else{ if(cur+5>=target){ oDiv.style.left=target+"px"; return; } cur+=5; } oDiv.style.left=cur+"px"; clearTimeout(oDiv.timer);//执行前先关闭定时器; oDiv.timer=setTimeout(_moveBat,30);//将timer设置在元素的自定义属性上,避免全局变量; } /* oDiv.timer=setTimeout(function () {//设置匿名函数后,会重复新建私有作用域,不能释放,不利于优化 moveBat(target); },30);*/ } //1 边界值的判断,预判是否到达边界值; //2 定时器的timer,设置在元素的自定义属性上,避免全局变量 //3 两个点击事件执行一个定时器时会出问题,所以需要在执行定时器之前,关闭定时器; //4 定时器中新建匿名函数的优化问题; </script> </body> </html>
- 知识点:
- 运动库linear函数封装
- 目的:获取运动元素的实时位置
- 参数:
- b:begin 运动起始位置
- c:change 还要走多远
- d:duration 走完剩下的路程需要的总时间
- t:time 代表走了多少时间
- 变量:time值为可变值,不断地累加,然后计算出实时位置;配合定时器使用;
- 返回值:返回实时位置值;
<script> function linear(c,d,t,b) { return c/d*t+b; } </script>
- 实例
<script> function linear(c,d,t,b) { return c/d*t+b; } var oDiv1=document.getElementById("div1");//运动元素 var oDiv2=document.getElementById("div2");//目标位置元素 //目标值target需要确定 var target=oDiv2.offsetLeft-oDiv1.offsetWidth-oDiv2.offsetWidth;//减去运动元素的宽度和自身的宽度; var b=oDiv1.offsetLeft; var c=target-b; var d=1000; var t=0; oDiv1.timer=setInterval(function () { t+=10;//time为变量,不断累加; //边界点判断 if(t>d){ oDiv1.style.left=target+"px"; clearInterval(oDiv1.timer); } var curLeft=linear(c,d,t,b); oDiv1.style.left=curLeft+"px"; },10) </script>
- 实例:一个物体的多运动
- 目的:实现一个物体在x,y两个方向上的同时匀速运动;
- 思路:
- 分别求出物体运动在x,y方向上的起始位置,目标位置,算出各自方向上的总路程;
- 设定总时间duration;
- 起始时间为0,即time初始赋值为0;
- 设置定时器:1)进行time变量的累加,最好跟定时器的执行时间相同;2)边界点判断(设置目标位置、停止定时器、阻断程序执行);3)利用linear方法获取x,y方向上的实时位置;4)设置实时位置;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>一个物体的多运动</title> <style> *{ margin: 0; padding: 0; } #div1{ width: 100px; height: 100px; line-height: 100px; text-align: center; font-size: 30px; color: blue; border-radius: 50%; background-color: red; position: absolute; left: 300px; top: 30px; } .div2{ width: 10px; height: 500px; position: absolute; top: 30px; left: 1050px; background-color: blue; } </style> </head> <body> <div id="div1">物体</div> <div class="div2" id="div2"></div> <script src="utils.js"></script> <script> function linear(c,d,t,b) { return c/d*t+b; } var oDiv1=document.getElementById("div1"); var oDiv2=document.getElementById("div2"); var targetLeft=oDiv2.offsetLeft-oDiv1.offsetWidth,targetTop=oDiv2.offsetHeight+oDiv2.offsetTop-oDiv1.offsetHeight; var beginLeft=oDiv1.offsetLeft,beginTop=oDiv1.offsetTop; var changLeft=targetLeft-beginLeft,changTop=targetTop-beginTop; var duration=1000; var time=0; var timer=setInterval(function () { //1 变量累加 time+=10; //2 边界点判断 if(time>=duration){ //设置目标值 utils.css(oDiv1,{ left:targetLeft, top:targetTop }); //停止定时器 clearInterval(timer); //阻断程序执行 return ; } //3 获取当前位置 var curLeft=linear(changLeft,duration,time,beginLeft); var curTop=linear(changTop,duration,time,beginTop); //4 设置当前位置 utils.css(oDiv1,{ left:curLeft, top:curTop }); },10); </script> </body> </html>
- 实例:封装方法,实现物体的多运动库
- 参数: ele:元素,target:目标值,对象,duration:运动总时间
- 注意点:
- 工具库的引用,需要按照引用顺序调用;
- 在工具库中利用自执行函数封装的方法,需要设置window,将其设置成全局变量,这样才能在全局引用;
- 工具库代码:
(function () { var gbEffect={ Linear:function(c,d,t,b){ return c/d*t+b; } }; function move(ele,target,duration){ //ele:元素,target:目标值,对象,duration:时间 duration=duration || 2000; var begin={},change={}; for(var attr in target){ begin[attr]=utils.css(ele,attr); change[attr]=target[attr]-begin[attr]; } var time=0; //定时器 var timer=setInterval(function () { //变量累加 time+=10; //边界点判断 if(time>=duration){ //设置边界值 console.log(1) utils.css(ele,target); //停止定时器 clearInterval(timer); //阻断程序执行 return ; } //获取实时位置 for(var attr in change){ var linear=gbEffect.Linear(change[attr],duration,time,begin[attr]); //设置实时位置 utils.css(ele,attr,linear); } },10) } //自执行函数中与外界无联系,利用window设置为全局 window.animate=move; })();
- 执行代码:
<body> <div id="div1">物体</div> <div id="div2"></div> <script src="utils.js"></script> <script src="move.js"></script> <script> var oDiv1=document.getElementById("div1"); var oDiv2=document.getElementById("div2"); animate(oDiv1,{ left:oDiv2.offsetLeft-oDiv1.offsetWidth, top:oDiv2.offsetTop+oDiv2.offsetHeight-oDiv1.offsetHeight, opacity:0.8 },2000) </script> </body>