promise的学习
promise是es6的新特性,也是最终的特性之一,我写这篇文章的目的是为了记录以便复习,和大家共同学习
期间我在学习的过程中参考的博文链接我放在下面
https://www.cnblogs.com/lvdabao/p/es6-promise-1.html#
参考的视频有黑马对promise的讲解
promise的作用
主要用于解决地狱回调函数
我们在初步使用promise的时候经常会使用到.then .catch 等一些原型方法 还有resolve, reject的回调函数
我们下面直接创建一个promise对象来深入了解一下 promise是什么
通过上面这个图我们可以看到Promise是一个构造函数,自己身上有all、reject、resolve这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。
promise更多的是对异步的处理
下面我们来看一下当我们不使用promise,来读取文件
我们使用nodejs的内置fs来读取文件,我们想要文件按顺序读取的时候就要用到一层一层嵌套,也就是套完 这样问题是解决了
但是我们的代码可读性不高,而且日后想要修改的时候也很困难,牵一发而动全身
那么我们如何来使用promise来去解决这个问题呢
我们来自己封装一个读取文件的promise对象
function readFile1(){
let p=new Promise((resolve,reject)=>{
fs.readFile('./text/tx1.txt','utf8',(err,DataStr)=>{
console.log('读取了file1')
resolve(DataStr)
})
})
return p
}
看这段我们封装一个读取文件函数 返回值是一个promise对象 那么当我们调用这个函数的时候我们实际得到的是一个promise对象,
这也就说明我们可以调用promise的.then()方法 同时这里我先讲解一下Resolve这个方法
resolve
我们可以先将resolved理解成 当我们读取成功后,我们可以将值传给resolve, 再通过.then()进行回调 拿到成功后的值
但实际在Promise中resolved代表一种状态,resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected。
这下你应该也能够理解到promise为什么叫做promise了 它是一种承诺,一旦确定为一种状态就不能改变
下面我们再理解一下.then方法
.then()方法是我们对promise中执行结果的一个预处理方法,我们可以事先处理我们在成功后要做什么,在失败后要做什么
所以then中可以传入两个参数,一个reolve回调函数,是对成功的处理,一个是reject回调函数,是对失败的处理。
在then方法中还有一个重要的特点,我们可以在resolve中继续返回一个新的promise对象,那么我们可以在这个then方法后继续使用then方法
这是一个重点,决定这我们如何去处理异步函数,如何去解决回调地狱
下面我们来看一下优化后的代码
readFile1().then(
(data)=>{
console.log(data);
return readFile2()
}
).then((data)=>{
console.log(data)
return readFile3()
}).then((data)=>{
console.log(data);
})
是不是看着更加优雅。而且解决了我们的问题 对于异步函数的处理 当然我们这里是利用读取文件进行演示的,你可以参考我上面放的链接中的利用
定时函数进行模拟
对于promise 你应该有个初步的理解了吧 可能刚开始看的时候确实比较迷 我刚开始也 我尼玛这是啥玩意
放一个完整代码吧 方便大家参考
const fs =require('fs')
function readFile1(){
let p=new Promise((resolve,reject)=>{
fs.readFile('./text/tx1.txt','utf8',(err,DataStr)=>{
console.log('读取了file1')
resolve(DataStr)
})
})
return p
}
function readFile2(){
let p=new Promise((resolve,reject)=>{
fs.readFile('./text/tx2.txt','utf8',(err,DataStr)=>{
console.log('读取了file2')
resolve(DataStr)
})
})
return p
}
function readFile3(){
let p=new Promise((resolve,reject)=>{
fs.readFile('./text/tx3.txt','utf8',(err,DataStr)=>{
console.log('读取了file3')
resolve(DataStr)
})
})
return p
}
readFile1().then(
(data)=>{
console.log(data);
return readFile2()
}
).then((data)=>{
console.log(data)
return readFile3()
}).then((data)=>{
console.log(data);
})
##别急我们再来讲解一下reject的使用
当我们读取我我文件发生错误 我们可以将错误的信息传给rejected,当我们读取出错时候我们就可以拿到错误信息
const fs =require('fs')
function readFile1(){
let p=new Promise((resolve,reject)=>{
fs.readFile('./text/tx6.txt','utf8',(err,DataStr)=>{
console.log('读取了file1')
if(err) return reject(err.message)
resolve(DataStr)
})
})
return p
}
readFile1().then(
// 成功的处理
(data)=>{
console.log(data);
},
// 失败的处理
(reason,data)=>{
console.log('reject')
console.log(reason);
}
)
打印出来的结果
这样错误信息就被打印出来了
相信你现在对resolve和reject的用法已经有点思路,多多思考 最好可以将我演示的代码敲一遍出来
下面我们来学习一下.catch方法来
catch
就是捕获错误信息,如果你学过java我信息你现在心里已经知道他是干什么的了
我们可以在开头捕获,和结尾捕获错误的信息 但是这两种写法还是有很大的区别的
关于cache的写法
效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,
如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中
我们对上面的代码进行改造后
readFile1().then(
// 成功的处理
(data)=>{
console.log(data);
},
// 失败的处理
// (reason,data)=>{
// console.log('reject')
// console.log(reason);
// }
).catch((reason)=>{
console.log('rejected');
console.log(reason);
})
结果
看到这里你知道如何使用了吗
我上面还说过catch写在开头 和结尾有很大区别 我们一起来看看
放在结尾
readFile1().then(
(data)=>{
console.log(data);
return readFile2()
}
).then((data)=>{
console.log(data)
return readFile3()
}).then((data)=>{
console.log(data);
}).catch(
(reason)=>{
console.log('rejected');
console.log(reason);
}
)
放在开头
readFile1().catch(
(reason)=>{
console.log('rejected');
console.log(reason);
}
).then(
(data)=>{
console.log(data);
return readFile2()
}
).then((data)=>{
console.log(data)
return readFile3()
}).then((data)=>{
console.log(data);
})
通过比较你发先catch在不同地方的区别了吗
在开头捕获到错误剩下的代码会依然执行
但是在结尾如果一遇到错误会立马执行catch里面的语句
下面我们再来介绍一下 Promise.all(),和 Promise.race()方法
all的用法
Promise
.all([readFile1(), readFile2(), readFile3()])
.then(function(results){
console.log(results);
});
用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,
等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,
all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。所以上面代码的输出结果就是:
有了all,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,是不是很酷?有一个场景是很适合用这个的,
一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后
,我们再进行页面的初始化。
race用法
all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,那么相对的就有另一个方法「谁跑的快,以谁为准执行回调」,
这就是race方法,这个词本来就是赛跑的意思。
下面我们将readfile1函数延迟一秒执行
Promise
.race([readFile1(), readFile2(), readFile3()])
.then(function(results){
console.log(results);
})
结果
这三个异步操作同样是并行执行的。结果你应该可以猜到,1秒后runAsync1已经执行完了,此时then里面的就执行了。
这个race有什么用呢?使用场景还是很多的,比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作,代码如下:
//请求某个图片资源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'xxxxxx';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
requestImg函数会异步请求一张图片,我把地址写为"xxxxxx",所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。
我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。
如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。运行结果如下:
好了对于promise我也仅仅是个初学者
如果了解更多请看开头的链接,如果有什么想交流的请留言