-
[前言]
-
[一、回调地狱是什么?]
-
[二、如何解决回调地狱]
-
- [1.Promise]
- [2.async/await]
-
[总结] 一. 回调地狱是什么
-
js中或者node,都大量的使用了回调函数进行异步操作,而异步操作什么是返回结果是不可控的
-
如果我们希望几个异步请求按照顺序来执行,那么就需要将这些异步操作嵌套起来
-
嵌套的层数特别多,就会形成横向金字塔,也叫做回调地狱
说人话:代码中回调函数嵌套回调函数,这种回调函数中嵌套回调函数的情况就回调地狱
小结一下: 回调地狱就是为了实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差.后期不好维护
二. 如何解决回调地狱
promise是js中一个原生对象,它是一个ES6提出一个新语法,用来优化异步代码的写法,可以替换掉传统的回调函数解决方案
promise使用如下
var p1 = new Promise(function(resolve,reject){
//异步操作 resolve(obj1) 或者 reject(obj2)
});
p1.then(function(rs){
// 如果p1的状态是resolved,则then中的函数
//会执行,且obj1的值会传给rs
}).catch(function(rs){
// 如果p1的状态是reject,则catch中的函数
// 会执行,且obj2的值会传给rs
}).finally(function(){
// 一定会执行的函数
})
then方法的链式调用
- 如果前一个then返回一个普通值,则后一个then可以得到这个值
- 如果前一个then返回一个 Promise对象,则后一个then可以得到 Promise对象成功状态的结果
构造器
// const obj = new Object()
// const arr = new Array()
const p1 = new Promise(function(resolve,reject){
// 执行异步代码
// 调用resolve,或者reject
});
console.dir(p1)
要点:
- 构造器必须要给定一个参数,如果不给就是会报错。例如,
new Promise()报错的信息是:Promise resolver undefined is not a function - 构造器的实参是一个函数,这个函数的特殊之处在于它有两个形参(resolve,reject),这两个形参也是函数。在格式上,也可以采用箭头函数来改写。例如:
var p1 = new Promise((resolve,reject)=>{})。 - 在函数体的内部, 一般会执行异步代码,然后根据情况来调用resolve()或者是reject() 。调用resolve或者是reject后会产生什么样的后果,在后面小节介绍。 当然了,再次强调一下resolve和reject只是形参名,可以改写成其它的。
promise的三种状态
then的返回值(难点)
then()方法的返回值也是一个promise对象,所以它支持链式写法。但是要注意的是它的返回值是一个新的promise对象,与调用then方法的并不是同一个对象。
看下如下代码:
var p1 = new Promise(()=>{});
var p2 = p1.then(function f_ok(){}, function f_err(){});
// p2也是一个promise对象。
console.log(p1 === p2); // false
如上代码可以说明p1.then()的结果是一个与p1不同的promise对象。换句话说,then()会封装一个全新的promise对象p2。那既然 p2也是一个promise对象,那么,p2的状态(promiseStatus)和值(promiseValue)分别是什么?
p2的状态及promiseValue如何确定?
p2的状态及promiseValue按如下规则来确定
- 如果p1的状态是pending,则p2的状态也是pending。
- 如果p1的状态是resolved,then()会去执行f_ok,则p2的状态由f_ok的返回值决定。
-
- 如果f_ok返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_ok函数的return值。
- 如果f_ok返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。
- 如果f_ok这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。
- 如果p1的状态是rejected,then()会去执行f_err,则p2的状态由f_err的返回值决定。
-
- 如果f_err返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_err函数的return值。
- 如果f_err返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。
- 如果f_err这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。
示例代码1
var p1 = new Promise(()=>{});
var p2 = p1.then(function f_ok(){}, function f_err(){}); // p2也是一个promise对象。
console.dir(p1); // pending
console.dir(p2); // pending
示例代码2
var p1 = new Promise((resolve,reject)=>{ resolve()});
var p2 = p1.then(function f_ok(){
return 1
}, function f_err(){}); // p2也是一个promise对象。
console.dir(p1); // resolved, undefined
console.dir(p2); // resolved, 1
特殊地,如果f_ok()中并没有return语句,则相当于是 return undefined。
示例代码3
var p1 = new Promise((resolve,reject)=>{ resolve()});
var p2 = p1.then(function f_ok(){
var temp = new Promise((resolve,reject)=>{ resolve({a:1}) });
return temp;
}, function f_err(){});
console.dir(p2); // resolved, {a:1}
示例代码4
var p1 = new Promise((resolve,reject)=>{ resolve()});
var p2 = p1.then(function f_ok(){
console.log(abc);// 这里故意犯错
}, function f_err(){});
console.dir(p2);
示例代码5
var p1 = new Promise((resolve,reject)=>{ reject(100)});
var p2 = p1.then(function f_ok(){
}, function f_err(errVal){
var temp = new Promise((resolve,reject)=>{ resolve({b:errVal}) });
return temp;
});
console.dir(p2);
示例代码6
var p1 = new Promise((resolve,reject)=>{ reject(100)});
var p2 = p1.then(function f_ok(){
}, function f_err(errVal){
throw new Error("aaa")
});
console.dir(p2)
async-await语法
- async和await是 ES2017 中提出来的
- async和await两个关键字的出现,简化的Promise的使用
- async用于修饰一个function
- async修饰的函数,总是返回一个Promise对象
- 函数的返回值,将自动包装在resolve的 promise中
- await只能出现在async函数内
- await让JS引擎等待直到promise完成并返回结果
- ·语法: let value = await promise对象;//等待promise对象的结果,然后将结果赋值给value
- 由于await需要等待promise执行完毕,所以await会暂停函数的执行,但不会影响其他同步任务
- // 1.await只能出现在async修饰的函数中!
- // 2.await后面跟随的是一个promise对象;
- // 3.await能停止代码执行,让后面的同步代码,先执行;
- // 4.await返回的是: Promise对象中的then)中的回调函数中的参数res;
示例
//封装一个返回promise的异步任务
function fn(str) {
var p = new Promise(function (resolve, reject) {
var flag = true;
setTimeout(function () {
if (flag) {
resolve(str)
} else {
reject('处理失败')
}
})
})
return p;
}
//封装一个执行上述异步任务的async函数
async function test(){
var res1=await fn('前端要以和为贵'); //await直接拿到fn()返回的promise的数据,并且赋值给res
var res2=await fn('要讲武德');
var res3=await fn('不要搞窝里斗');
console.log(res1,res2,res3);
}
//执行函数
test(); // 前端要以和为贵 要讲武德 不要搞窝里斗
总结一下:
当我们写代码遇到异步回调时,我们想让异步代码按照我们想要的顺序执行,如果按照传统的嵌套方式,就会出现回调地狱,这样的代码不利于维护,我们可以通过Promise对象进行链式编程来解决,这样尽管可以解决问题,但是ES7给我们提供了更加舒适的async/await语法糖,可以使得异步代码看起来更像是同步代码。