回调函数获取异步结果
//喝奶茶的方法
function getTea(fn){
setTimeout(()=>{
fn('奶茶')
},1000)
}
//吃火锅的方法
function getHotpot(fn){
setTimeout(()=>{
fn('火锅')
},2000)
}
//调用,输出上:理论上先喝奶茶再吃火锅
getTea((data) =>{console.log(data)})
getHotpot((data) =>{console.log(data)})
//调用,输出上:要先吃火锅再喝奶茶(通过嵌套调用)
getHotpot((data)=>{console.log(data);
getTea((data)=>{console.log(data);
getTea((data)=>{console.log(data);
...})})})
...为了实现指定顺序输出,多次嵌套出现 回调地狱
Promise处理异步
//使用resolve将异步结果传递出来,通过then拿到异步结果
//喝奶茶的方法
function getTea(){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('奶茶')
},1000)
})
}
//吃火锅的方法
function getHotpot(){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('火锅')
},2000)
})
}
//调用喝奶茶
getTea().then((data)=>{console.log(data)})
//getTea可以拿到promise对象,promise对象有then方法可以拿到调用resolve传出来的异步结果
//先吃火锅再喝奶茶
getHotpot((data)=>{console.log(data);
return getTea()})//注意在前一个then中return promise对象后面才可继续then拿到异步结果
.then((data)=>{console.log(data)})
.then((data)=>{console.log(data)})
.then((data)=>{console.log(data)})
.then((data)=>{console.log(data)})
...
注意:调用resolve后才能then拿到传出来的异步结果
也即resolve出来的值是then回调中的形参
async函数处理异步
async function getData(){
const hotPot = await getHotpot()
//await可以直接获取resolve传递出来的异步数据,相当于promise.then
}
//指定顺序,先吃火锅再喝奶茶,直接按同步的逻辑写即同步的顺序
async function getData(){
const hotPot = await getHotpot()
console.log(hotPot)
const Tea = await getTea()
console.log(Tea)
}
注意:async函数的返回值是promise对象
async funtion fun(){
return 1
}
//等价于
function fun(){
return new Promise((resolve)=>{
resolve(1)
})
}
//如何拿到fun返回值 1呢?
//由于fun返回值是promise对象,1是被promise对象包装的promise对象,具体拿到值需要用then回调
fun().then((res)=>{console.log(res)})
async function fun1(){
const data = await fun2()
console.log(data) //可以看作是then中执行的代码,因为上方await会拿到fun2()具体的值才返回
}
async function fun2(){
console.log(200)
return 100
}
fun1()//200 100
async和await妙用
async函数返回一个promise对象,只需要在调用它的时候后面接上then做进一步操作
await后面接的一般是promise对象,await 返回值是promise对象成功后的返回值相当于promise.then,一般赋值给一个变量
await会阻塞进程,会暂停当前 async函数 的执行,等待 Promise 处理完成
有await必须搭配async使用,有async不一定必需await
const a = async () => {
let n = Promise.reject(123)
try{
await n; //可以在try中await,以便捕获错误,或者直接在fn调用后接catch
console.log(n)
}catch(err){
console.error(err)
}
return n //添加此行则返回的promise对象中reject状态能显示promise结果,
否则显示undefined且uncaught
//若要具体拿到123值则需要在调用a方法后.catch回调中进行具体操作,否则async函数永远返回promise对象
// 因为.catch回调是promise实例方法
}
a().then(..).catch(...)
.then的第二个参数 和 .catch的区别引出思考
1、reject是用来抛出异常的、说明promise错误的原因,类似thorw new error(xxx);catch是处理异常的,平级于then
2、reject是Promise的方法,如Promise.reject(xxx),返回的仍是Promise对象,是带有reject状态的Promise对象,若在async函数中将Promise.reject(xxx)赋值给一个变量并return出去,则在reject状态的Promise对象中能查看到reject的原因即xxx,而then和catch是promise实例的方法,then回调中可放回调参数,回调参数用于具体操作xxx具体值的,then和catch返回的也是一个带状态的Promise对象
3、如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到,因为then的第二个函数和第一个函数是平级的
4、then的第二个参数和catch捕获错误信息的时候会就近原则,如果是promise内部报错,reject抛出错误后,then的第二个参数和catch方法都存在的情况下,只有then的第二个参数能捕获到,如果then的第二个参数不存在,则catch方法会捕获到
5、catch可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch方法,而不使用then方法的第二个参数
Promise状态理解
Promise.resolve和Promise.reject
可以理解为是一种快捷创建promise的resolve/reject状态的方法
一个resolved/fulfilled状态的promise会触发then回调函数的第一个回调函数参数
一个rejected状态的promise会触发catch回调函数
(promise不同状态下可以触发的不同回调函数的情况)
.then/.catch 回调中没有抛出异常,完成回调,则会返回一个resolved/fulfilled状态的promise对象,而resolved/fulfilled状态的promise对象则会触发.then回调函数的第一个回调函数参数,如此往下
一旦抛出异常,返回的都是一个rejected状态的promise对象,而rejected状态的promise对象则会触发 .catch回调函数,如此往下
如此以来,形成的链式调用是“职责链”,不是形式上的调用链
职责链小测试:
setTimeout彻悟
setTimeout(fn,1000)表示:在1000ms后,尽快执行fn
setTimeout(fn,0)表示:在0ms后,尽快执行fn,而不是表示立马执行fn,否则没有意义
补充:函数的参数本质上是复制的,如 实参传给形参 即在函数体内部声明一个变量将实参复制给形参变量
try、catch 的同步和异步捕获错误
在异步程序中可以不使用 try-catch 配合 async/await 来处理错误,但是处理方式并不能与 async/await 配合得很好
try catch一般用于同步的错误捕获,如代码中出现async异步任务,需要使用try catch捕获错误的话,必须在try中使用await等待接收到异步任务返回的结果 才能捕获到错误,如不使用try catch仍想捕获错误的话,可以考虑封装一种函数拥有promise的便捷错误处理和await/async的简洁的写法,即
const response = await ajax('/xxx').catch(handleError)
//记得要在handleError 里 reject 或throw