通过回调、自定义事件、promise、Async函数四种方法实现异步操作方格运动的案例

205 阅读1分钟

首先创建一个方格

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box {
            width: 100px;
            height: 100px;
            position: absolute;
            left: 0px;
            top: 0px;
            background: red;
        }
    </style>
</head>

<body>
    <div class="box"></div>
</body>

一、回调

 <script>
     //四个参数分别是事件、目标位置、方向、回调函数
    function move(ele, target, dir, cb) {
        //获取最开始的left值,并parseInt()取整
        let startLeft = parseInt(getComputedStyle(ele, null)[dir]);
        // console.log(startLeft)
       
        //设置一个速度,通过初始值和目标值的差值正负关系来决定运动方向
        let speed = (target - startLeft) > 0 ? 1 : -1;

        setTimeout(() => {
            //给初始值加上速度
            startLeft += speed;
            if (startLeft === target) {
                //当和目标值相同,运动完成
                // 可以打印console.log("运动完成");
                //通过回调函数来执行异步函数
                cb && cb();
            } else {
                //给事件的left赋值
                ele.style[dir] = startLeft + "px";
                //加上循环,运动完成后不会再调用move
                move(ele, target, dir, cb);
            }
        }, 10);
    }

    // requestAnimationFrame性能比setTimeout性能更好

    //获取事件
    let ele = document.querySelector(".box");
    //传入一个方向,"left"和"top"能控制运动方向
    //下面两个是同步执行的,不能先右移动再下移动,而是右下斜着移动
    // move(ele, 300,"left");
    // move(ele, 300,"top");

    // 通过回调解决异步;回调地狱(函数嵌套关系太复杂,后期难以维护)
    move(ele, 300, "left", function () {
        move(ele, 300, "top", function () {
            move(ele, 0, "left", function () {
                move(ele, 0, "top", function () {
                    console.log("运动完成");
                })
            })
        })
    })
 </script>

二、自定义事件(观察者模式)

<script>
    //首先自定义一个事件
    let eventObj = new EventTarget();
    //设置一个变量,为了后面区分事件名
    let num = 1;

    //不需要回调了,删除cb
    function move(ele, target, dir) {
        let startLeft = parseInt(getComputedStyle(ele, null)[dir]);
        // console.log(startLeft)
        // let speed =(target-startLeft)/ Math.abs( target-startLeft ) ; 这是通过绝对值来确定+-1
        let speed = (target - startLeft) > 0 ? 1 : -1;
        setTimeout(() => {
            startLeft += speed;
            if (startLeft === target) {
                // console.log("运动完成");
                // cb && cb(); 不用上个方法的回调了
                //通过下面方法触发自定义事件,myevent+num是事件名称,num++设置不同的事件名
                eventObj.dispatchEvent(new CustomEvent("myevent"+num));
                num++;
            } else {
                ele.style[dir] = startLeft + "px";
                move(ele, target, dir);
            }
        }, 10);
    }

    let ele = document.querySelector(".box");

    //调取move函数
    move(ele, 300, "left");
   
    //绑定事件
    eventObj.addEventListener("myevent1",function(){
        console.log("事件触发,运动完成myevent1");
        move(ele, 300, "top");
    });
    eventObj.addEventListener("myevent2",function(){
        console.log("事件触发,运动完成myevent2");
        move(ele, 0, "left")
    });
    eventObj.addEventListener("myevent3",function(){
        console.log("事件触发,运动完成myevent3");
        move(ele, 0, "top")
    });
    eventObj.addEventListener("myevent4",function(){
        console.log("事件触发,运动完成myevent4");
    });

    //优点:没有嵌套  缺点:实现起来麻烦,代码冗余

</script>

三、Promise对象

<script>
function move(ele, target, dir) {
        //返还一个promise对象
        return new Promise(resolve => {
            //用一个函数把之前的move封装起来,防止返还很多promise对象
            function fn() {
                let startLeft = parseInt(getComputedStyle(ele, null)[dir]);
                // console.log(startLeft)
                // let speed =(target-startLeft)/ Math.abs( target-startLeft ) ;
                let speed = (target - startLeft) > 0 ? 1 : -1;
                setTimeout(() => {
                    startLeft += speed;
                    if (startLeft === target) {
                        // console.log("运动完成");
                        // cb && cb();
                        //返还一个完成提示
                        resolve("运动完成");
                    } else {
                        ele.style[dir] = startLeft + "px";
                        //不再使用回调
                        fn();
                    }
                }, 10);
            }
            //调用这个内部函数
            fn();
        })
    }

    let ele = document.querySelector(".box");

    // 通过一层层的then方法实现链式操作
    move(ele, 300, "left").then(res=>{
        //console.log(res);
        //执行完第一次,返回一个函数,继续执行相同的操作,以此类推,直到所有操作完成
      return  move(ele, 300, "top");
    }).then(res=>{
       // console.log(res);
        return move(ele, 0, "left");
    }).then(res=>{
        return  move(ele, 0, "top");
    }).then(res=>{
        console.log("所有运动完毕");
    })
 </script>

四、Async函数

<script> 
function move(ele, target, dir) {
        //返还一个promise对象
        return new Promise(resolve => {
            //用一个函数把之前的move封装起来,防止返还很多promise对象
            function fn() {
                let startLeft = parseInt(getComputedStyle(ele, null)[dir]);
                // console.log(startLeft)
                // let speed =(target-startLeft)/ Math.abs( target-startLeft ) ;
                let speed = (target - startLeft) > 0 ? 1 : -1;
                setTimeout(() => {
                    startLeft += speed;
                    if (startLeft === target) {
                        // console.log("运动完成");
                        // cb && cb();
                        //返还一个完成提示
                        resolve("运动完成");
                    } else {
                        ele.style[dir] = startLeft + "px";
                        //不再使用回调
                        fn();
                    }
                }, 10);
            }
            //调用这个内部函数
            fn();
        })
    }

let ele = document.querySelector(".box");
//上面这部分和promise方法一样

//先声明一个async函数
 async function asyncFn(){
        try{
            //使用await 操作符依次执行操作
            await move(ele, 300, "left");
            await move(ele, 300, "top");
            await move(ele, 0, "left");
            await move(ele, 0, "top");
        }catch(e){
            console.log(e);
        }
     //可以用catch捕获失败的操作
    }

//最后调用这个函数
asyncFn();

</script>