promise对象-代替回调函解决异步操作
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种状态:
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,取决于程序运行时程序处理顺序 });