回调地狱和Promise
1.回调函数的作用及回调地狱
先说一下什么是回调函数的作用
- 1.1在js中我们会使用很多的异步方法,例如定时器,ajax还有node中的fs
- 1.2 很多时候我们需要拿到这个异步函数里面的一些数据,但是通过一般的手段我们并不能拿到(js单线程)这个跟他的执行机制有关,我们可以通过回调函数的方式拿到这个数据例如:
add = (x,y,callback) => {
setTimeout(function(){
const ret = x + y;
callback(ret)
},1000)
})
add(1,2,(ret)=>{
console.log(ret)
}
//我们想要拿到这个str的值通过一般的方法并不是一件容易的事,
//如上我们就可以这样拿到这个异步函数的里面的数据
- 1.3 如果有多个异步函数属于同级关系,他们的执行顺序是没有先后的,但是在实际操作中我们会遇到一些情况,我们需要上一个异步请求的结果作为我们下一个异步请求的值传入,但是又因为执行顺序的问题而不能正常使用,因此就有了回调地狱这个说法,如(网上一个很形象的图)
我再以node中的fs举例
const fs = require('fs')
fs.readFile('a.txt','utf8',(err,data)=>{
if(err){
throw err
}
console.log(data)
})
fs.readFile('b.txt','utf8',(err,data)=>{
if(err){
throw err
}
console.log(data)
})
fs.readFile('c.txt','utf8',(err,data)=>{
if(err){
throw err
}
console.log(data)
})
这个时候我们读取这三个文件,但是又想要让其有先后的执行顺序(a,b,c),我们只能这样
const fs = require('fs')
fs.readFile('a.txt', 'utf8', (err, data) => {
if (err) {
throw err
}
console.log(data)
fs.readFile('b.txt', 'utf8', (err, data) => {
if (err) {
throw err
}
console.log(data)
fs.readFile('c.txt', 'utf8', (err, data) => {
if (err) {
throw err
}
console.log(data)
})
})
})
- 1.4 把读取文件b函数放在a的函数内部,在把读取文件c函数的放在b的函数内部,这就是我们所谓的回调地狱。
接下来我们说一下他的坏处:这个代码看着很难受,很不美观,函数一层套着一层,很不利于我们日常的维护由于现在这个情况还没有那么复杂,但是在我们实际开发的过程中,如果出现大量的这样的代码可想而知…😓…😓…
2.Promise使用
因此在ES6中Promise诞生了(I promise you)
- 我先用一段代码展示一下使用了promise之后的异步操作( 这个是封装后的,最下面有没有封装的可能更容易理解 )。
const fs = require('fs')
pReadFile = (filePath) => {
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
reject(err)
}
resolve(data)
})
})
}
//我这个为了方便就把then中的第二个参数省略了
pReadFile('a.txt')
.then((data) => {
console.log(data) // a.txt
return pReadFile('b.txt')
})
.then((data) => {
console.log(data) //b.txt
return pReadFile('c.txt')
})
.then((data) => {
console.log(data) //c.txt
})
Promise这种采用了链式调用的方法,使代码看起来十分的简洁,这样维护起来就会更加方便。
我这里就只说一下用法原理的话,后面会继续写另一篇,包括自己实现一个Promise。
- 2.1 我们可以通过
new Pomise()
产生一个Promise对象,并且new 出来这个对象 它是立即执行的。 - 2.2 在Promise 我们使用它向其中传入一个函数如下
new Promise( (resolve, reject) => {} )
,这其中resolve代表已解决,reject代表失败。 - 2.3 我们可以把需要执行的异步操作放入这个函数中,然后通过这两种状态来返回数据,成功了就调用resolve,失败了就调用reject。如下:
const p1 = new Promise((resolve, reject) => {
fs.readFile('a.txt', 'utf8', (err, data) => {
if (err) {
reject(err) //这个函数会在.then中的第二个参数执行
}
resolve(data) //这个函数会在.then 中的第一个参数执行
// console.log(data)
})
})
p1.then((data) => {
console.log(data)
}, (err) => {
console.log(err)
})
//then 中的第一个参数就是resolve()
//这个函数这个函数传什么下面then中的函数的参数
//第二个参数计师reject那个函数
- 2.4 then中有两个参数,
p1.then(function(data), function(err) )
第一个参数是成功的回调,第二个是失败的回调。 - 2.5 当然这个时候只这样的话应该还看不懂上面的那个示例,p1那里为什么能连续的.then呢?这个时候就要说一下这个then方法了,在这个then方法中我们可以return 一个值 ,然后这个值 可以作为下一个 then的resolve ,例如
xxx.then( data => { return 'hello'}).then( data => console.log(data) ) //hello
.。 - 当然如果只是上面那种情况肯定是没有用的,我们并不是需要一个任意的值,因此他的真正的用处是这样的,
xxx.then(function(data) { return new Promise(xxx) })
这个时候要注意这里return一个Promise对象但是下面再次.then的时候收到的值就不是这个return的值了,可能会感觉很拗口因此代码来解释。
const p1 = new Promise((resolve, reject) => {
fs.readFile('a.txt', 'utf8', (err, data) => {
if (err) {
reject(err) //这个函数会在.then中的第二个参数执行
}
resolve(data) //这个函数会在.then 中的第一个参数执行
// console.log(data)
})
})
const p2 = new Promise((resolve, reject) => {
fs.readFile('b.txt', 'utf8', (err, data) => {
if (err) {
reject(err) //这个函数会在.then中的第二个参数执行
}
resolve(data) //这个函数会在.then 中的第一个参数执行
// console.log(data)
})
})
//我这个为了方便就把then中的第二个参数省略了
p1
.then((data) => {
console.log(data) // a.txt的数据
return p2 //这个p2是上面定义好的p2是一个Promise对象
})
.then((data) => {
console.log(data) //b.txt的数据 , 这个的值是p2 中的 resolve
})
//每个then都会有两个参数,只不过我省略了 第二个参数 ,第二个参数 则是 p2中的reject
我们一般return 时并不会在全局中定义一个p2,而会这样,这两种写法效果是一样的。
p1
.then((data) => {
console.log(data) // a.txt的数据
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
reject(err)
}
resolve(data)
})
})
})
最后我们回到最开始使用Promise的那个案例上,那个案例就是把读文件的操作使用Promise封装了一下,如果不封装的如下可能更容易理解:
const p1 = new Promise((resolve, reject) => {
fs.readFile('a.txt', 'utf8', (err, data) => {
if (err) {
reject(err) //这个函数会在.then中的第二个参数执行
}
resolve(data) //这个函数会在.then 中的第一个参数执行
// console.log(data)
})
})
const p2 = new Promise((resolve, reject) => {
fs.readFile('b.txt', 'utf8', (err, data) => {
if (err) {
reject(err)
}
resolve(data)
// console.log(data)
})
})
const p3 = new Promise((resolve, reject) => {
fs.readFile('c.txt', 'utf8', (err, data) => {
if (err) {
reject(err)
}
resolve(data)
// console.log(data)
})
})
p1
.then((data) => {
console.log(data) //a.txt
return p2
})
.then((data) => {
console.log(data) //b.txt
return p3
})
.then((data) => {
console.log(data) //c.txt
})
如果还有不理解的可以在下方评论或者私信博主!
转载:https://blog.csdn.net/qq_44983621/article/details/104652690
查看评论