zoukankan      html  css  js  c++  java
  • ES6 Promise --回调与Promise的对比、信任问题、错误处理、Promise的状态、以及Promise对象的常用方法

    之前怎么用回调解决异步的问题:

    function f(callback){
        setTimeout(function(){
            callback && callback();
        });    
    }
    
    f(function(){
        console.log(1);
        f(function(){
            console.log(2);    
            f(function(){
                console.log(3);    
                f(function(){
                    console.log(4);    
                    f(function(){
                        console.log(5);    
                        f(function(){
                            console.log(6);        
                        })    
                    })        
                })    
            })    
        })
    })

    使用promise实现相同的效果

    //使用promise实现相同的效果
    function f2(){
        return new Promise(resolve=>{//参数传入一个回调函数
            setTimeout(function(){
                //时执行函数
                resolve();
            },1000)
        })
    }
    
    f2()//只有返回Promise实例,才能.then
    .then(function(){
        console.log(11);
        return f2();
    })
    .then(function(){
        console.log(22);
        return f2();
    })
    .then(function(){
        console.log(33);
        return f2();
    })
    .then(function(){
        console.log(44);
        return f2();
    })
    .then(function(){
        console.log(55);
        return f2();
    })
    .then(function(){
        console.log(66);
        return f2();
    })

    对比回调与Promise的流程控制

    首先是回调

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>index</title>
        <style>
            .box{
                100px;
                height:100px;
                background:lightgreen;
                transition:all 1s;
                color:#fff;
                text-align:center;
                line-height:100px;
                font-size:40px;
            }
        </style>
    </head>
    <body>
    
        <div class="box">哦</div>
        <button id="btn">开始</button>
    <script>
    //动画
    function move(el,x,y,cb){
        el.style.transform=`translate(${ x }px, ${ y }px)`;
        setTimeout(function(){
            cb && cb();
        },1000);    
    }
    
    //获取元素
    let box=document.querySelector(".box");
    let btn=document.querySelector("#btn");
    //绑定事件
    btn.addEventListener("click",e=>{
        //使用回调完成动画
        move(box,100,100,function(){
            move(box,200,200,function(){
                move(box,100,300,function(){
                    move(box,0,0,function(){
                        console.log("移动结束!");
                    })
                })    
            })    
        })    
    })
    
    </script>
    </body>
    </html>

    实现的效果

    使用Promise来实现

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>index</title>
        <style>
            .box{
                100px;
                height:100px;
                background:lightgreen;
                transition:all 1s;
                color:#fff;
                text-align:center;
                line-height:100px;
                font-size:40px;
            }
        </style>
    </head>
    <body>
    
        <div class="box">哦</div>
        <button id="btn">开始</button>
    <script>
    //动画
    function move(el,x,y){
        return new Promise(resolve=>{
            el.style.transform=`translate(${ x }px, ${ y }px)`;
            setTimeout(function(){
                resolve();
            },1000);
        })        
    }
    
    //获取元素
    let box=document.querySelector(".box");
    let btn=document.querySelector("#btn");
    //绑定事件
    btn.addEventListener("click",e=>{
        //使用Promise完成动画
        move(box,100,100)
        .then(function(){        
            return move(box,200,200);
        })
        .then(function(){        
            return move(box,100,300);
        })
        .then(function(){        
            return move(box,0,0);
        })
        .then(function(){
            console.log("移动结束!");
        })
    })
    
    </script>
    </body>
    </html>

    实现一个图片的加载;设置第一张图片加载1s之后加载第二张图片

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>index</title>
        <style>
            img{200px;}
        </style>
    </head>
    <body>
    
    
    <script>
    
    //设置一个函数,把图片的url地址作为参数
    function createImg(url){
        //实例化promise对象
        return new Promise(resolve=>{
            let img=new Image();//建立图像对象
            img.src=url;//设置图片的地址
            document.body.appendChild(img);//把图片节点插入到body中
            setTimeout(function(){
                resolve();//图片加载完成后执行resolve 
            },1000);
        })
    }
    
    createImg("1.jpg")
    .then(function(){
        return createImg("2.jpg")
    })
    .then(function(){
        return createImg("3.jpg")
    });
    
    </script>
    </body>
    </html>

    信任问题

    //信任问题演示
    
    //回调
    function method(cb){
        setTimeout(function(){
            cb && cb();
            //因为某些bug导致某个函数多执行了一次
            cb && cb();
        },1000);
    }
    
    //promise
    function method2(){
        return new Promise(resolve=>{
            setTimeout(function(){
                resolve();
                //resolve成功调用一次之后,后面的不会再执行
                resolve();
            },1000);        
        })
    }

    控制反转

    //回调
    function method(cb){
        setTimeout(function(){
            cb && cb.call({a:1,b:2});//执行回调,但是添油加醋
        },1000);
    }
    
    //promise
    function method2(){
        return new Promise(resolve=>{
            setTimeout(function(){
                resolve();//调用的resolve都是自己写的,改善了控制反转的问题
            },1000);        
        })
    }

    错误处理

    function fn(val){
        //第二个参数代表失败时做的事情
        return new Promise((resolve,reject)=>{
            if(val){
                resolve();
            }else{
                reject();
            }
        })
    }
    
    fn(false)
    .then(()=>{
        console.log("成功");
    },()=>{
        console.log("失败");
    })

    错误处理回调可以传入参数

    function fn(val){
        //第二个参数代表失败时做的事情
        return new Promise((resolve,reject)=>{
            if(val){
                resolve();
            }else{
                reject("404");
            }
        })
    }
    
    fn(false)
    .then(()=>{
        console.log("成功");
    },e=>{
        console.log(e);
    })

    resolve也可以传递参数,但是只能传一个,不能传两个

    function fn(val){
        //第二个参数代表失败时做的事情
        return new Promise((resolve,reject)=>{
            if(val){
                resolve({a:1},{b:2});
            }else{
                reject("404");
            }
        })
    }
    
    fn(true)
    .then((obj1,obj2)=>{
        console.log(obj1);
        console.log(obj2);
    },e=>{
        console.log(e);
    })

    使用实例的catch方法,可以捕获错误

    如果返回的是错误,则下面必须有对错误的捕获处理,否则代码不会执行,会被跳过

    function fn(val){
        //第二个参数代表失败时做的事情
        return new Promise((resolve,reject)=>{
            if(val){
                resolve("这是数据");
            }else{
                reject("404");
            }
        })
    }
    
    fn(true)
    .then(data=>{
        console.log(data);
        return fn(false);//失败,抛出错误
    })
    .then(()=>{
        console.log("这里没有对错误的处理,因此不会执行");
    })
    .catch(e=>{//捕获错误,执行代码
        console.log(e);
    })

    如果在捕获错误之前,存在对错误的处理,那么catch不会再执行

    function fn(val){
        //第二个参数代表失败时做的事情
        return new Promise((resolve,reject)=>{
            if(val){
                resolve("这是数据");
            }else{
                reject("404");
            }
        })
    }
    
    fn(true)
    .then(data=>{
        console.log(data);
        return fn(false);//失败,抛出错误
    })
    .then(()=>{
        console.log("这里没有对错误的处理,因此不会执行");
    })
    .then(()=>{
        
    },e=>{
        console.log("这里对错误进行了处理,下面的catch不会被执行了");
    })
    .catch(e=>{//捕获错误,执行代码
        console.log(e);
    })

    catch之后还可以继续then,如果再次抛出错误,也需要在之后进行错误处理

    function fn(val){
        //第二个参数代表失败时做的事情
        return new Promise((resolve,reject)=>{
            if(val){
                resolve("这是数据");
            }else{
                reject("404");
            }
        })
    }
    
    fn(true)
    .then(data=>{
        console.log(data);
        return fn(false);//失败,抛出错误
    })
    .then(()=>{
        console.log("这里没有对错误的处理,因此不会执行");
    })
    .catch(e=>{//捕获错误,执行代码
        console.log(e);
        return fn(false);//再次抛出错误
    });

    最后抛出的错误没有捕获,因此报错

    finally 不管成功或失败,都会执行

    function fn(val){
        //第二个参数代表失败时做的事情
        return new Promise((resolve,reject)=>{
            if(val){
                resolve("这是数据");
            }else{
                reject("404");
            }
        })
    }
    
    fn(true)
    .then(data=>{
        console.log(data);
        return fn(false);//失败,抛出错误
    })
    .catch(e=>{//捕获错误,执行代码
        console.log(e);
    })
    .finally(()=>{
        console.log("finally执行一些收尾工作");
    })

    Promise的状态

    panding 进行中

    fulfilled 成功

    reject 失败

    Promise.all()

    把多个promise实例,包装成一个新的promise实例

    如果所有promise结果都是成功,则返回成功,所有promise返回的数据以数组形式统一返回,且顺序一一对应

    如果有一个promise决议为失败,则返回失败,且把失败的信息返回

    如果是空数组,则立即决议为成功

    //模拟需要多个请求数据才能进行下一步的情况
    function data1(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data1加载成功");
                resolve("data1");//传递参数
            },1000)
        })
    }
    
    function data2(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data2加载成功");
                resolve("data2");//传递参数
            },1000)
        })
    }    
    
    function data3(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data3加载成功");
                resolve("data3");//传递参数
            },1000)
        })
    }    
    
    function data4(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data4加载成功");
                resolve("data4");//传递参数
            },2000)
        })
    }    
    
    //全部成功的情况
    let res=Promise.all([data1(),data2(),data3(),data4()]);
    res
    .then(data=>{
        console.log(data);//接收上面传递过来的所有参数
    })

    //模拟需要多个请求数据才能进行下一步的情况
    function data1(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data1加载成功");
                resolve("data1");//传递参数
            },1000)
        })
    }
    
    function data2(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                reject("data2 err");//数据2请求失败
            },1000)
        })
    }    
    
    function data3(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data3加载成功");
                resolve("data3");//传递参数
            },1000)
        })
    }    
    
    function data4(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data4加载成功");
                resolve("data4");//传递参数
            },2000)
        })
    }    
    
    //全部成功的情况
    let res=Promise.all([data1(),data2(),data3(),data4()]);
    res
    .then(data=>{
        console.log(data);//接收上面传递过来的所有参数
    },e=>{
        console.log(e);//有错误执行这句并立刻返回错误信息,正确的数据不会返回
    })

    //模拟需要多个请求数据才能进行下一步的情况
    function data1(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data1加载成功");
                resolve("data1");//传递参数
            },1000)
        })
    }
    
    function data2(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                reject("data2 err");//数据2请求失败
            },1000)
        })
    }    
    
    function data3(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data3加载成功");
                resolve("data3");//传递参数
            },1000)
        })
    }    
    
    function data4(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data4加载成功");
                resolve("data4");//传递参数
            },2000)
        })
    }    
    
    //全部成功的情况
    let res=Promise.all([]);
    res
    .then(()=>{
        console.log("决议成功");//空数组直接决议为成功
    },e=>{
        console.log(e);//有错误执行这句并立刻返回错误信息,正确的数据不会返回
    })

    相同的功能,使用ES6之前的语法,不使用promise.all()要如何实现:

    //不使用promise.all()
    
    let count=0;
    
    function fn(){
        if(count<4) return;
        console.log("数据全部接收成功");
    }
    
    function data1(){
        setTimeout(()=>{            
            console.log("data1加载成功");
            count++;
            fn();
        },2000)
    }
    
    function data2(){
        setTimeout(()=>{            
            console.log("data2加载成功");
            count++;
            fn();
        },2000)
    }    
    
    function data3(){
        setTimeout(()=>{            
            console.log("data3加载成功");
            count++;
            fn();
        },2000)
    }    
    
    function data4(){
        setTimeout(()=>{            
            console.log("data4加载成功");
            count++;
            fn();
        },2000)
    }    
    
    data1();
    data2();
    data3();
    data4();

    如果有数据接收失败

    //不使用promise.all()
    
    let count=0;
    let err=false;
    
    function fn(){
        if(err) {
            console.log("有数据接收失败");
            return;
        }
        if(count<4) return;
        console.log("数据全部接收成功");
    }
    
    function data1(){
        setTimeout(()=>{            
            console.log("data1加载成功");
            count++;
            fn();
        },2000)
    }
    
    function data2(){
        setTimeout(()=>{            
            console.log("data2加载失败");
            err=true;
            fn();
        },2000)
    }    
    
    function data3(){
        setTimeout(()=>{            
            console.log("data3加载成功");
            count++;
            fn();
        },2000)
    }    
    
    function data4(){
        setTimeout(()=>{            
            console.log("data4加载成功");
            count++;
            fn();
        },2000)
    }    
    
    data1();
    data2();
    data3();
    data4();

    Promise.race()

    数组中只要有一个决议为成功,则立马决议为成功,并把值传递过来

    //promise.race()
    function data1(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{    
                console.log("data1成功")    ;    
                resolve("data1");
            },1000)
        })
    }
    
    function data2(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{    
                console.log("data2成功")    ;            
                resolve("data2");
            },500)
        })
    }    
    
    function data3(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data3成功")    ;                
                resolve("data3");
            },1000)
        })
    }    
    
    function data4(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{    
                console.log("data4成功")    ;            
                resolve("data4");
            },1000)
        })
    }    
    
    let res=Promise.race([data1(),data2(),data3(),data4()]);
    res
    .then(data=>{
        console.log(data);//输出最早成功的数据
    },e=>{
        console.log(e);
    })

    如果有错误,也会立即输出err信息

    //promise.race()
    function data1(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{    
                console.log("data1成功")    ;    
                resolve("data1");
            },1000)
        })
    }
    
    function data2(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{    
                console.log("data2失败")    ;            
                reject("err2");
            },500)
        })
    }    
    
    function data3(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data3成功")    ;                
                resolve("data3");
            },1000)
        })
    }    
    
    function data4(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{    
                console.log("data4失败")    ;            
                resolve("err4");
            },1000)
        })
    }    
    
    let res=Promise.race([data1(),data2(),data3(),data4()]);
    res
    .then(data=>{
        console.log(data);//输出最早成功的数据
    },e=>{
        console.log(e);
    })

    如果传入空数组,则程序被挂起

    //promise.race()
    function data1(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{    
                console.log("data1成功")    ;    
                resolve("data1");
            },1000)
        })
    }
    
    function data2(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{    
                console.log("data2失败")    ;            
                reject("err2");
            },500)
        })
    }    
    
    function data3(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                console.log("data3成功")    ;                
                resolve("data3");
            },1000)
        })
    }    
    
    function data4(){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{    
                console.log("data4失败")    ;            
                resolve("err4");
            },1000)
        })
    }    
    
    let res=Promise.race([]);
    res
    .then(data=>{
        console.log(data);//输出最早成功的数据
    },e=>{
        console.log(e);
    })

    如果不使用ES6的promise.race(),实现效果如下

    //不使用promise.race()
    let flag=false;
    function fn(data){
        if(flag) return;
        flag=true;//有请求则返回true
        console.log(data);
    }
    
    function data1(){
        setTimeout(()=>{    
            console.log("data1成功")    ;    
            fn({name:1})
        },500)
    }
    
    function data2(){
        setTimeout(()=>{    
            console.log("data2成功")    ;
            fn({name:2})    
        },600)
    }    
    
    function data3(){
        setTimeout(()=>{    
            console.log("data3成功")    ;
            fn({name:3})    
        },1000)
    }    
    
    function data4(){
        setTimeout(()=>{    
            console.log("data4成功")    ;    
            fn({name:4})
        },2000)
    }    
    
    data1();
    data2();
    data3();
    data4();

    Promise.resolve() 不管传递什么值进去,都会包装成一个promise实例

    //promise.resolve()
    
    //传递一个普通值
    let p1=new Promise(resolve=>{
        console.log("p1决议成功");
    })
    
    let p2=Promise.resolve("p2成功");//传递一个普通的值,直接决议为成功
    
    //传递一个promise实例
    let p11=new Promise(resolve=>{
        console.log("p11决议成功");
    })
    
    let p22=Promise.resolve(p11);//传递一个promise实例,使得p11和p22相等
    p11.then(data=>void console.log(data));
    console.log(p11===p22);
    
    
    //定义一个thenable对象obj
    let obj={
        then(cb){
            console.log("成功")
            cb("成功啦")
        },
        oth(){
            console.log("失败")
        }
    }
    //Promise.resolve(obj) 传递一个thenable对象
    Promise.resolve(obj).then(data=>{
        console.log(data)
    })

    Promise.reject()

    不管传递什么值,拿到的都是传入的值,不会进行操作和处理

    //promise.reject()
    
    //传递一个thenable对象obj
    Promise.reject({then(){console.log("err")}})
    .then(function(){
        console.log("我不会被执行");
    },e=>{
        console.log(e)
    })

    resolve是异步任务,会在所有同步任务完成后执行

    console.log(1);
    
    let p=new Promise(resolve=>{
        console.log(2);
        resolve();//调用resolve相等于调用.then,是异步执行,在所有同步完成后执行
        console.log(3);
    })
    
    console.log(4);
    
    p.then(()=>{
        console.log(5);
    })
    
    console.log(6);

    把同步任务转为异步任务

    function fn(cb){
        //返回一个决议成功的实例,并异步执行
        return Promise.resolve(cb).then(cb=>cb());
    }
    
    fn(()=>{
        console.log("我从同步变成了异步");
        return 1+1;
    }).then(res=>{
        console.log(res);//拿到return的值
    })
    
    console.log("我是同步");

    小案例

    页面中有多个板块,需要所有图片加载完成后再显示

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>es6 promise</title>
    </head>
    <style>
        img{height:100px;}
    </style>
    <body>
    <script>
    
    
    
    const loadImg=(src)=>{
        return new Promise((resolve,reject)=>{
            let img=new Image();
            img.src=src;
            //图片加载成功
            img.onload=()=>{
                resolve(img)
            }
            //图片加载失败
            img.onerror=(e)=>{
                reject(e)
            }
            //注意这种写法是错误的,因为赋值时直接被调用,还没有等待图片加载已经执行完毕了
            // img.onload=resolve(img)
            // img.onerror=reject(e)
        })
    }
    
    const imgs=["1.jpg","2.jpg","3.jpg"];
    // map通过遍历把src作为参数传入,循环调用loadImg,获取到返回的image对象
    Promise.all(imgs.map(src=>loadImg(src)))
    .then(res=>{
        console.log(res);
        //遍历插入DOM
        res.forEach(item=>{
            document.body.appendChild(item)
        })
    })
    
    </script>
    </body>
    </html>

    失败的情况

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>es6 promise</title>
    </head>
    <style>
        img{height:100px;}
    </style>
    <body>
    <script>
    
    
    
    const loadImg=(src)=>{
        return new Promise((resolve,reject)=>{
            let img=new Image();
            img.src=src;
            //图片加载成功
            img.onload=()=>{
                resolve(img)
            }
            //图片加载失败
            img.onerror=(e)=>{
                reject(e)
            }
            //注意这种写法是错误的,因为赋值时直接被调用,还没有等待图片加载已经执行完毕了
            // img.onload=resolve(img)
            // img.onerror=reject(e)
        })
    }
    
    const imgs=["1.jpg","22.jpg","3.jpg"];
    // map通过遍历把src作为参数传入,循环调用loadImg,获取到返回的image对象
    Promise.all(imgs.map(src=>loadImg(src)))
    .then(res=>{
        console.log(res);
        //遍历插入DOM
        res.forEach(item=>{
            document.body.appendChild(item)
        })
    })
    .catch(e=>{
        console.log(e)
    })
    
    </script>
    </body>
    </html>

  • 相关阅读:
    [NOIP2010]引水入城
    [NOIP2009]靶形数独
    设计模式学习笔记——桥接模式(Bridge)
    BootStrap3.0学习--JavaScript 插件
    BootStrap3.0学习--组件
    BootStrap3.0学习--全局 CSS 样式
    BootStrap3.0学习--起步
    设计模式之第11章-建造者模式(Java实现)
    设计模式之第10章-桥接模式(Java实现)
    设计模式之第9章-原型模式(Java实现)
  • 原文地址:https://www.cnblogs.com/chenyingying0/p/12578285.html
Copyright © 2011-2022 走看看