小言_互联网的博客

回调地狱和Promise使用(详解)

279人阅读  评论(0)

回调地狱和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
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场