回调函数获取异步结果

  1. //喝奶茶的方法
  2. function getTea(fn){
  3. setTimeout(()=>{
  4. fn('奶茶')
  5. },1000)
  6. }
  7. //吃火锅的方法
  8. function getHotpot(fn){
  9. setTimeout(()=>{
  10. fn('火锅')
  11. },2000)
  12. }
  13. //调用,输出上:理论上先喝奶茶再吃火锅
  14. getTea((data) =>{console.log(data)})
  15. getHotpot((data) =>{console.log(data)})
  16. //调用,输出上:要先吃火锅再喝奶茶(通过嵌套调用)
  17. getHotpot((data)=>{console.log(data);
  18. getTea((data)=>{console.log(data);
  19. getTea((data)=>{console.log(data);
  20. ...})})})
  21. ...为了实现指定顺序输出,多次嵌套出现 回调地狱

Promise处理异步

  1. //使用resolve将异步结果传递出来,通过then拿到异步结果
  2. //喝奶茶的方法
  3. function getTea(){
  4. return new Promise((resolve)=>{
  5. setTimeout(()=>{
  6. resolve('奶茶')
  7. },1000)
  8. })
  9. }
  10. //吃火锅的方法
  11. function getHotpot(){
  12. return new Promise((resolve)=>{
  13. setTimeout(()=>{
  14. resolve('火锅')
  15. },2000)
  16. })
  17. }
  18. //调用喝奶茶
  19. getTea().then((data)=>{console.log(data)})
  20. //getTea可以拿到promise对象,promise对象有then方法可以拿到调用resolve传出来的异步结果
  21. //先吃火锅再喝奶茶
  22. getHotpot((data)=>{console.log(data);
  23. return getTea()})//注意在前一个then中return promise对象后面才可继续then拿到异步结果
  24. .then((data)=>{console.log(data)})
  25. .then((data)=>{console.log(data)})
  26. .then((data)=>{console.log(data)})
  27. .then((data)=>{console.log(data)})
  28. ...
  29. 注意:调用resolve后才能then拿到传出来的异步结果
  30. 也即resolve出来的值是then回调中的形参

async函数处理异步

  1. async function getData(){
  2. const hotPot = await getHotpot()
  3. //await可以直接获取resolve传递出来的异步数据,相当于promise.then
  4. }
  5. //指定顺序,先吃火锅再喝奶茶,直接按同步的逻辑写即同步的顺序
  6. async function getData(){
  7. const hotPot = await getHotpot()
  8. console.log(hotPot)
  9. const Tea = await getTea()
  10. console.log(Tea)
  11. }
  12. 注意:async函数的返回值是promise对象
  1. async funtion fun(){
  2. return 1
  3. }
  4. //等价于
  5. function fun(){
  6. return new Promise((resolve)=>{
  7. resolve(1)
  8. })
  9. }
  10. //如何拿到fun返回值 1呢?
  11. //由于fun返回值是promise对象,1是被promise对象包装的promise对象,具体拿到值需要用then回调
  12. fun().then((res)=>{console.log(res)})
  13. async function fun1(){
  14. const data = await fun2()
  15. console.log(data) //可以看作是then中执行的代码,因为上方await会拿到fun2()具体的值才返回
  16. }
  17. async function fun2(){
  18. console.log(200)
  19. return 100
  20. }
  21. fun1()//200 100

async和await妙用

async函数返回一个promise对象,只需要在调用它的时候后面接上then做进一步操作
await后面接的一般是promise对象,await 返回值是promise对象成功后的返回值相当于promise.then,一般赋值给一个变量
await会阻塞进程,会暂停当前 async函数 的执行,等待 Promise 处理完成
有await必须搭配async使用,有async不一定必需await

  1. const a = async () => {
  2. let n = Promise.reject(123)
  3. try{
  4. await n; //可以在try中await,以便捕获错误,或者直接在fn调用后接catch
  5. console.log(n)
  6. }catch(err){
  7. console.error(err)
  8. }
  9. return n //添加此行则返回的promise对象中reject状态能显示promise结果,
  10. 否则显示undefineduncaught
  11. //若要具体拿到123值则需要在调用a方法后.catch回调中进行具体操作,否则async函数永远返回promise对象
  12. // 因为.catch回调是promise实例方法
  13. }
  14. 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不同状态下可以触发的不同回调函数的情况)
image.png
.then/.catch 回调中没有抛出异常,完成回调,则会返回一个resolved/fulfilled状态的promise对象,而resolved/fulfilled状态的promise对象则会触发.then回调函数的第一个回调函数参数,如此往下
一旦抛出异常,返回的都是一个rejected状态的promise对象,而rejected状态的promise对象则会触发 .catch回调函数,如此往下
如此以来,形成的链式调用是“职责链”,不是形式上的调用链

职责链小测试:
image.pngimage.pngimage.png

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的简洁的写法,即

  1. const response = await ajax('/xxx').catch(handleError)
  2. //记得要在handleError 里 reject 或throw