JS-11-es6常用知识-Promise(6K字超级详解!!)
文章目录
1 回调地狱
2 Promise函数基本用法
3 Promise函数实现多层回调
4 Promise传参
5 Promise 错误处理
5.1 两种错误处理方式
5.2 catch捕获错误
5.3 多层异步种使用catch
6 使用 Promise 的优势
1 回调地狱
1)为什么要有promise?
解决(回调地狱)的问题
2)什么是回调?
回调(Callback)是一种编程模式,其中一段可执行的代码(即回调函数)作为参数传递给另一个函数(即主函数),并在需要时由主函数执行。这种机制允许主函数在特定事件或条件满足时,调用并执行之前注册的回调函数。
function greet(name, callback) { console.log(`Hello, ${name}!`); callback(); // 调用回调函数 } function sayGoodbye() { console.log('Goodbye!'); } greet('Alice', sayGoodbye); // 输出 "Hello, Alice!" 和 "Goodbye!"
在这个示例中,
greet
函数接受一个name
参数和一个callback
回调函数作为参数。当greet
函数被调用时,它会先输出一条问候语,然后调用callback
回调函数。在这个例子中,sayGoodbye
函数被作为回调函数传递给greet
函数,并在greet
函数内部被调用。3)什么是回调地狱?
回调地狱(Callback Hell)是指在编写异步JavaScript代码时,回调函数嵌套的现象越来越深,导致代码的可读性变差、维护性变差、复杂度增加、错误率增加等问题。回调地狱通常表现为深度嵌套并且难以阅读的代码,例如:
//跟以前的if条件地狱很像 // if(){ // if(){ // if(){ // } // } // } $.get("/getUser",function(res){ $.get("/getUserDetail",function(){ $.get("/getCart",function(){ $.get("/getBooks",function(){ //... }) }) }) }) //node开发:读取文件;开个服务器、接收一个请求、请求路径、访问数据库
2 Promise函数基本用法
例1:下面代码是一个使用 Promise 来封装异步操作(在这个例子中是使用 jQuery 的
$.get
方法来发送一个 GET 请求到 “/getUser”)的例子:执行顺序:abcdvar promise=new Promise((resolve,reject)=>{ //b 把需要执行的异步操作放在这里 $.get("/getUser",res=>{ //获取数据的异步操作已经执行完毕了,等待下一步的执行,通过执行resolve函数,告诉外界你可以执行下一步操作了 //c、 resolve(res) //而执行的下一步操作,其实就是写在then的回调函数中的 }) }) //a、 promise.then(res=>{ //d、执行后续的操作 console.log(res); })
1)创建 Promise
var promise = new Promise((resolve, reject) => { // ... });
这里,创建了一个Promise 实例。Promise 是一个对象,它代表一个最终可能完成(也可能被拒绝)的异步操作及其结果值。Promise 构造函数接受一个执行器函数,这个函数有两个参数:
resolve
和reject
。这两个函数都是用于改变 Promise 状态的。2)执行异步操作
$.get("/getUser", res => { // ... resolve(res); });
在 Promise 的执行器函数内部,使用 jQuery 的
$.get
方法发送了一个 GET 请求到 “/getUser”。当请求成功时,jQuery 会调用提供的回调函数,并传入响应数据res
。在这个回调函数中,调用了resolve
函数,并将响应数据res
作为参数传递给它。这表示异步操作已经成功完成,并且这里有一个结果值要返回给 Promise 的调用者。3)处理 Promise 的结果
promise.then(res => { // ... console.log(res); });
使用
promise.then()
方法,你指定了一个回调函数来处理 Promise 的结果。当 Promise 的状态变为 “fulfilled”(即resolve
函数被调用)时,这个回调函数会被执行,并且resolve
函数的参数(在这个例子中是res
)会被作为回调函数的参数。在这个例子中,回调函数只是简单地打印了响应数据res
到控制台。例2:读代码,感受promise的基本用法
3 Promise函数实现多层回调
–解决回调地狱
例1:-> 获取用户 -> 获取用户基本信息 -> 查看用户详情 -> 查看购物车信息
new Promise((resolve,reject)=>{ $.get("/getUser",res=>{ resolve(res) }) }).then(res=>{ //用户基本信息 return new Promise(resolve=>{ $.get("/getUserDetail",res=>{ resolve(res) }) }) }).then(res=>{ //用户详情 return new Promise(resolve=>{ $.get("/getCart",res=>{ resolve(res) }) }) }).then(res=>{ //购物车信息 })
例2:第一步 -> 第二步 -> 第一步 -> 第二步 -> 完成
4 Promise传参
function getUser(){
return new Promise(resolve=>{
$.get("/getUser",res=>{
//res是从服务器中接收到的数据
//把数据传到下一步操作中
//告诉外界本次的异步操作已经执行完毕了
resolve(res)
})
})
}
getUser().then(res=>{
//res就表示上一个异步操作返回的参数值:从服务器中获取的数据
})
5 Promise 错误处理
5.1 两种错误处理方式
第一种方式,then里包含成功、失败两个返回值
new Promise((resolve,reject)=>{ $.ajax({ url:"/getUser", type:"GET", success:res=>{ resolve(res); }, error:res=>{ reject(res) } }) }).then(resSuccess=>{ //成功的返回值 },resError=>{ //失败的返回值 })
第二种方式:catch(推荐)
new Promise((resolve,reject)=>{ $.ajax({ url:"/getUser", type:"GET", success:res=>{ resolve(res); }, error:res=>{ reject(res) } }) }).then(resSuccess=>{ //成功的返回值 }).catch(resError=>{ //失败的返回值 })
小结:上面2种错误处理的方式,第二种更加推荐。第二种方式更强大的地方在于:
a、不仅仅可以捕获到reject传递的参数
b、还可以捕获到:成功的回调中发生的错误
样例:
5.2 catch捕获错误
catch不仅能捕获错误信息,还能捕获到成功后的代码错误信息
setTimeout
来模拟异步操作。这个异步操作将在 1 秒(1000 毫秒)后完成function f1(name){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ if(name=="a"){ resolve("成功"); }else{ reject("失败") } },1000) }) }
then里处理
f1("b").then(res=>{ console.log('成功了'); },resError=>{ console.log('失败了'); })
catch处理
f1("b").then(res=>{ console.log('成功了'); }).catch(res=>{ console.log('失败了'); })
catch处理成功信息里出现代码错误
f1("a").then(res => { var a = 5; a(); //这里代码发生了错误 }).catch(res => { //成功的捕捉到了成功回调中的代码错误 console.log(res); })
效果(打印错误,控制台没出现红色报错信息)
5.3 多层异步种使用catch
例1:首先执行第一步-> 成功-> 第二步-> 失败->
new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log('第一步'); resolve("第一步完成") },100) }).then(res=>{ console.log(res); //res:第一步完成 return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log('第二步'); reject('第二步失败'); },100) }) }).then(res=>{ //并不会执行到这里 console.log('第二步成功'); }).catch(res=>{ console.log(res); })
效果:
拓展: axios就是一个基于Promise封装出来的进行ajax请求的库
axios.get("/getUser").then(res=>{
return axios.get("/getUserDetail")
}).get("/getLoginInfo").then(res=>{
console.log('');
})
6 使用 Promise 的优势
代码结构更清晰:Promise 允许我们将异步操作的组织方式从传统的嵌套回调(callback hell)转变为链式调用,这使得代码结构更加清晰,易于理解和维护。
错误处理更简洁:传统的嵌套回调中,错误处理通常需要在每个回调函数中显式地添加错误处理逻辑。而 Promise 提供了
.catch()
方法,可以一次性捕获多个异步操作中的错误,使错误处理更加简洁和统一。支持链式调用:Promise 支持链式调用,可以通过
.then()
方法将多个异步操作串联起来,形成一个执行流程。这使得异步代码更加易于组织和管理,提高了代码的可读性和可维护性。更好的可读性和可维护性:Promise 的语法更接近于同步代码,使得异步代码的阅读和编写更加直观。此外,Promise 还支持 ES6 的
async/await
语法,可以将异步代码转换为看起来更像同步代码的形式,进一步提高了代码的可读性和可维护性。更好的返回值处理:Promise 的
resolve
方法可以传递任何类型的值(包括其他 Promise),这使得我们可以更加灵活地处理异步操作的返回值。在多层回调中,通常需要手动处理每个回调函数的返回值,而在 Promise 中,我们可以使用.then()
方法来统一处理返回值。可取消的异步操作(虽然 Promise 本身不直接支持取消,但可以通过扩展 Promise 或者使用其他库来实现):在某些情况下,我们可能需要取消正在进行的异步操作。虽然 Promise 本身并不直接支持取消操作,但我们可以通过扩展 Promise 或者使用其他库(如 Bluebird)来实现取消功能。
更好的组合和复用:Promise 提供了
Promise.all()
、Promise.race()
等静态方法,允许我们更方便地对多个 Promise 进行组合和复用。这些功能在多层回调中很难实现。