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,取决于程序运行时程序处理顺序
});

 

 

 

 

 

posted @ 2018-07-19 22:47  黑熊君  阅读(796)  评论(0编辑  收藏  举报