zoukankan      html  css  js  c++  java
  • promise对象代替回调函解决异步操作

    问题:
    服务器中有3个文件a.txt、b.txt、c.txt分别存储的数据为1、2、3,通过js中的异步ajax,分别获取这3个文件的内容并求和输出6。
     
    问题答案方法一(回调函数方法):(对应下面的:问题答案方法二)
    第一步:先封装ajax,用来读取文件中的数据
    function get(url, fn){
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.send();
        xhr.onreadystatechange = function(){
            if( xhr.readyState==4 && xhr.status==200 ){
                if( fn ){
                    fn(xhr.responseText);
                }
            }
        }    
    }
    //这一步不用管只知道执行下面代码就可以读取a.txt中的数据即可
    //get("a.txt", function(data){
    //    console.log(data);//输出 1
    //});

     第二步(错误示例):没有使用回调函数

    var a, b, c;
    get( "a.txt", function(data){
        a = Number( data );
    } );
    get( "b.txt", function(data){
        b = Number( data );
    } );
    get( "c.txt", function(data){
        c = Number( data );
    } );
    console.log(a+b+c); //输出 NaN
    //由于ajax请求是异步的所以最后一行代码先执行输出了 NaN

     第三步:使用回调函数解决异步处理(上面的问题完成了)

    var a, b, c;
    get( "a.txt", function(data){
        a = Number( data );
        get( "b.txt", function(data){
            b = Number( data );
            get( "c.txt", function(data){
                c = Number( data );
                console.log( a+b+c );//输出 6
            } );
        } );
    } );

     但是:

    不论是浏览器中最为常见的ajax、事件监听,还是node中文件读取、网络编程、数据库等操作,都离不开异步编程。在异步编程中,许多操作都会放在回调函数(callback)中。同步与异步的混杂,回调函数会层层嵌套,而这种层层嵌套的写法形成类似金字塔形状,被称为:“回调地狱”、“回调函数噩梦”和“恶魔金字塔”。过多的回调嵌套使得代码变得难以理解与维护,此时就要想办法改进这种代码形式,于是promise被创造出来了。

    ------------------------------------------------------promise对象-------------------------------------------------------

    promise对象是一种思维方式,是一种代替回调函数解决异步任务中,代码执行循序的问题(等看完篇再回来理解)。

    为什么要代替回调函数:过多的回调函数层层嵌套使得代码变得难以理解与维护(回调地狱)。

    一:promise是一个状态机,提供了3种状态:

    Pending:进行中
    Resolved:已完成,又称Fulfilled
    Rejected:已失败

    promise被创建出来的时候,它的状态是进行中,我们可以通过调用promise的resolve或rejet方法来调用我们自己定义的相关的函数。(别着急等看完下面代码)

    promise中then方法的使用:

    var p = new Promise(function( resolve, rejet ){//参数是两个函数(状态)
       //当调用resolve或reject其中一个就会跳出去执行then方法中对应的函数
        resolve();//当调用resolve的时候,then方法中的第一个函数就会执行
       reject();//当调用reject的时候,then方法中的第二个函数就会执行
    });
    p.then( function(){}, function(){} ); //then方法中的两个函数分别对应resolve, rejet

    举例:

    1.调用resolve()

    var p = new Promise(function( resolve, rejet ){
        resolve();
    });
    p.then(function(){
        console.log("已完成");
    }, function(){
        console.log("已失败");
    });//输出:已完成

     2.调用reject()

    var p = new Promise(function( resolve, rejet ){
        reject();
    });
    p.then(function(){
        console.log("已完成");
    }, function(){
        console.log("已失败");
    });//输出:已失败

     3.如果我们使用时,只需要调用第一个resolve(),可以简写为:

    而resolve(data)中可以输入参数,并可以传入到then方法中的函数进行接收

    var data = "传入的参数";
    var p = new Promise(function( resolve ){
        resolve(data);
    });
    p.then(function(data){
        console.log(data);
    });//输出:传入的参数

    既然promise只有调用resolve()才能执行then中的函数,就可以代替回调函数,读取并输出a.txt文件:(用链式方法表示)

    new Promise(function(resolve){
        get("a.txt", function(data){
            resolve(data);
        });
    }).then(function(data){
        console.log(data);
    });//输出 1

    问题答案方法二(promise方法):所以一开始的1+2+3问题就可以这样解决:(在then的函数中return一个promise对象就可以接着在后面进行链式操作)

    var a, b, c;
    new Promise( function( resolve ){
        get( "a.txt", function(data){
            resolve(data);
        } );
    } ).then(function(data){
        a = Number(data);
        return new Promise(function(resolve){//在这里return一个promise对象
            get("b.txt", function(data){
                resolve(data);
            });
        });//如果不return,就必须在这里使用then方法,因为then方法是promise对象的
    }).then(function(data){//前面的return作用就是可以在这里接着使用then方法
        b = Number(data);
        return new Promise(function(resolve){
            get("c.txt", function(data){
                resolve(data);
            })
        })
    }).then(function(data){
        c = Number(data);
        console.log(a+b+c);//输出 6
    });

    有人就问了:用promise方法还不如回调函数方法看起来简单容易理解,那么我们就要进行封装一个promise函数来简化

    //promise函数封装
    var promise_get = function(url){
        return new Promise( function(resolve, reject){
            get(url, function(data){
                resolve(data);
            });
        });
    }

    那么上面求6的复杂代码就变成了这样:(这就是代替回调函数解决异步操作的方法)

    var a, b, c;
    promise_get("a.txt").then(function(data){
        a = Number(data);
        return promise_get("b.txt");
    }).then(function(data){
        b = Number(data);
        return promise_get("c.txt");
    }).then(function(data){
        c = Number(data);
        console.log(a+b+c);//输出 6
    });

    但是promise有一种方法求和6更为简单(all方法):下面就介绍all方法和race方法的使用和区别

    问题答案方法二(promise中all方法):

    var  p1 = promise_get("a.txt");
    var  p2 = promise_get("b.txt");
    var  p3 = promise_get("c.txt");
    
    //所有的promise对象的状态都发生变化之后,then里面的函数才能被执行
    //result接受的是所有的promise对象的传回的数据,它是一个数组
    //谁最后执行完,以谁为准执行回调
    Promise.all( [p1, p2, p3] ).then(function(result){
        console.log( eval( result.join("+") ) );//输出 6,eval()是js中可以将里面的字符串转化成执行语句的方法,arr.join("+")方法可以将数组按照指定字符拼接成字符串
    });//这样5行代码求和就更加简单明亮了
    
    //在众多的promise对象中,哪一个promise对象的状态先被改变了
    //result接受的就是这个promise对象的传回的数据
    //谁最先执行完,以谁为准
    Promise.race( [p1, p2, p3] ).then(function(result){
        console.log( result );//输出 1或2或3,取决于程序运行时程序处理顺序
    });

     

  • 相关阅读:
    git版本控制入门
    SpringBoot入门基础:编写HelloWorld(三)
    SpringBoot入门基础:构建SpringBoot项目及启动器讲解(二)
    SpringBoot入门基础:介绍(一)
    maven入门基础:为项目分配独立的仓库实践(十七)
    maven入门基础:nexus的权限管理(十六)
    maven入门基础:使用maven部署构件到nexus(十五)
    maven入门基础:配置maven从nexus下载构件(十四)
    maven入门基础:nexus构建搜索(十三)
    事务的写法
  • 原文地址:https://www.cnblogs.com/luowenshuai/p/9338719.html
Copyright © 2011-2022 走看看