async/await介绍

1. 基于Promise的async/await

async/await缺一不可,他们的出生是为Promise服务的。可以说async/awaitPromise的进化版。

为什么要有async/await缺一不可,他们的出生是为Promise服务的。可以说async/await是Promise的进化版。**存在呢?

async/await 的目的是简化使用多个 promise 时的同步行为,并对一组 Promise
执行某些操作。为了解决大量复杂不易读的Promise异步的问题,才出现的改良版。

那么先说一下async

async function process() {}

上面可以看出,async必须声明的是一个function,不要去声明别的,要是那样await就会报错。

这样声明也是错的!

const async demo =  function () {} // 错误

必须紧跟着function。接下来说一下await

上面说到必须是个函数(function),那么await就必须是在这个async声明的函数内部使用,否则就会报错。

就算你这样写,也是错的。

let data = 'data'
demo  = async function () {
    const test = function () {
        await data
    }
}

必须是直系(作用域链不能隔代),这样会报错:Uncaught SyntaxError: await is only valid in async function。

2. async的本质

async声明的函数的返回本质上是一个Promise

什么意思呢?就是说你只要声明了这个函数是async,那么内部不管你怎么处理,它的返回肯定是个Promise

看下列例子:

(async function () {
    return '我是Promise'
})()
// 返回是Promise
//Promise {<resolved>: "我是Promise"}

你会发现返回是这个:Promise {<resolved>: "我是Promise"}。
自动解析成 Promise.resolve('我是Promise');

等同于:

(async function () {
    return Promise.resolve('我是Promise');
})()

所以你想像一般function的返回那样,拿到返回值,原来的思维要改改了!你可以这样拿到返回值:

const demo = async function () {
    return Promise.resolve('我是Promise');
    // 等同于 return '我是Promise'
    // 等同于 return new Promise((resolve,reject)=>{ resolve('我是Promise') })
}
demo().then(result=>{
    console.log(result) // 这里拿到返回值
})

上述三种写法都行,要看注释细节都写在里面了!!像对待Promise一样去对待async的返回值!!!

3. await的本质与例子

await的本质是可以提供等同于”同步效果“的等待异步返回能力的语法糖

举例:

const demo = async ()=>{
    let result = await new Promise((resolve, reject) => {
      setTimeout(()=>{
        resolve('我延迟了一秒')
      }, 1000)
    });
    console.log('我由于上面的程序还没执行完,先不执行“等待一会”');
}
// demo的返回当做Promise
demo().then(result=>{
  console.log('输出',result);
})

await顾名思义就是等待一会,只要await声明的函数还没有返回,**那么下面的程序是不会去执行的!!!**这就是字面意义的等待一会(等待返回再去执行)。

那么你到这测试一下,你会发现输出是这个:输出 undefined。这是为什么呢?

你在`demo`函数里面都没声明返回,哪来的`then`?所以正确写法是这样:
const demo = async ()=>{
    let result = await new Promise((resolve, reject) => {
      setTimeout(()=>{
        resolve('我延迟了一秒')
      }, 1000)
    });
    console.log('我由于上面的程序还没执行完,先不执行“等待一会”');
    return result;
}
// demo的返回当做Promise
demo().then(result=>{
  console.log('输出',result); // 输出 我延迟了一秒
})

推荐的写法是带上then,规范一点,当然你没有返回也是没问题的,demo会照常执行。下面这种写法是不带返回值的写法:

const demo = async ()=>{
    let result = await new Promise((resolve, reject) => {
      setTimeout(()=>{
        resolve('我延迟了一秒')
      }, 1000)
    });
    console.log('我由于上面的程序还没执行完,先不执行“等待一会”');
}
demo();

所以可以发现,只要你用await声明的异步返回,是必须“等待”到有返回值的时候,代码才继续执行下去。

那事实是这样吗?你可以跑一下这段代码:

const demo = async ()=>{
    let result = await setTimeout(()=>{
      console.log('我延迟了一秒');
    }, 1000)
    console.log('我由于上面的程序还没执行完,先不执行“等待一会”');
    return result
}
demo().then(result=>{
  console.log('输出',result);
})

你会发现,输出是这样的:

由于上面的程序还没执行完,先不执行“等待一会”
输出 1
我延迟了一秒

奇怪,并没有awaitsetTimeout是异步,问题在哪?问题就在于setTimeout这是个异步,但是不是Promise!起不到“等待一会”的作用。

所以更准确的说法应该是用await声明的Promise异步返回,必须“等待”到有返回值的时候,代码才继续执行下去。

请记住await是在等待一个Promise的异步返回

当然这种等待的效果只存在于“异步”的情况,await可以用于声明一般情况下的传值吗?

事实是当然可以:

const demo = async ()=>{
    let message = '我是声明值'
    let result = await message;
    console.log(result); 
    console.log('我由于上面的程序还没执行完,先不执行“等待一会”');
    return result
}
demo().then(result=>{
  console.log('输出',result);
})

输出:

我是声明值
我由于上面的程序还没执行完,先不执行“等待一会”
输出 我是声明值

这里只要注意一点:then的执行总是最后的。

4. async/await 优势实战

现在我们看一下实战:

const setDelay = (millisecond) => {
  return new Promise((resolve, reject)=>{
      if (typeof millisecond != 'number') reject(new Error('参数必须是number类型'));
      setTimeout(()=> {
        resolve(`我延迟了${millisecond}毫秒后输出的`)
      }, millisecond)
  })
}
const setDelaySecond = (seconds) => {
  return new Promise((resolve, reject)=>{
      if (typeof seconds != 'number' || seconds > 10) reject(new Error('参数必须是number类型,并且小于等于10'));
      setTimeout(()=> {
        resolve(`我延迟了${seconds}秒后输出的,注意单位是秒`)
      }, seconds * 1000)
  })
}

比如上面两个延时函数(写在上面),比如我想先延时1秒,在延迟2秒,再延时1秒,最后输出“完成”,这个过程,如果用then的写法,大概是这样(嵌套地狱写法):

setDelay(1000)
.then(result=>{
    console.log(result);
    return setDelaySecond(2)
})
.then(result=>{
    console.log(result);
    return setDelay(1000)
})
.then(result=>{
    console.log(result);
    console.log('完成')
})
.catch(err=>{
    console.log(err);
})

乍一看是不是挺繁琐的?如果逻辑多了估计看得更累,现在我们来试一下async/await

(async ()=>{
  const result = await setDelay(1000);
  console.log(result);
  console.log(await setDelaySecond(2));
  console.log(await setDelay(1000));
  console.log('完成了');
})()

是不是没有冗余的长长的链式代码,语义化也非常清楚,非常舒服,那么你看到这里,一定还发现了,上面的catch我们是不是没有在async中实现?接下去我们就分析一下async/await如何处理错误?

5. async/await错误处理

因为async函数返回的是一个Promise,所以我们可以在外面catch住错误。

const demo = async ()=>{
  const result = await setDelay(1000);
  console.log(result);
  console.log(await setDelaySecond(2));
  console.log(await setDelay(1000));
  console.log('完成了');
}
demo().catch(err=>{
    console.log(err);
})

async函数的catch中捕获错误,当做一个Pormise处理,同时你不想用这种方法,可以使用try...catch语句

(async ()=>{
  try{
    const result = await setDelay(1000);
    console.log(result);
    console.log(await setDelaySecond(2));
    console.log(await setDelay(1000));
    console.log('完成了');
  } catch (e) {
    console.log(e); // 这里捕获错误
  }
})()

当然这时候就不需要在外面catch了。

通常我们的try...catch数量不会太多,几个最多了,如果太多了,说明你的代码肯定需要重构了,一定没有写得非常好。还有一点就是try...catch通常只用在需要的时候,有时候不需要catch错误的地方就可以不写。

try...catch好像只能包裹代码块,如果我需要拆分开分别处理,不想因为一个的错误就整个process都crash掉了,那么难道要写一堆try...catch吗?下面有一种很好的解决方案,仅供参考:

await后面跟着的肯定是一个Promise,那是不是可以这样写?

(async ()=>{
  const result = await setDelay(1000).catch(err=>{
      console.log(err)
  });
  console.log(result);
  const result1 = await setDelaySecond(12).catch(err=>{
      console.log(err)
  })
  console.log(result1);
  console.log(await setDelay(1000));
  console.log('完成了');
})()

这样输出:

我延迟了1000毫秒后输出的
Error: 参数必须是number类型,并且小于等于10
    at Promise (test4.html:19)
    at new Promise (<anonymous>)
    at setDelaySecond (test4.html:18)
    at test4.html:56
undefined
我延迟了1000毫秒后输出的
完成了

是不是就算有错误,也不会影响后续的操作,但是这样写得别扭await又跟着catch。那么可以改进一下,封装一下提取错误的代码函数:

// to function
function to(promise) {
   return promise.then(data => {
      return [null, data];
   })
   .catch(err => [err]); // es6的返回写法
}

返回的是一个数组,第一个是错误,第二个是异步结果,使用如下:

(async ()=>{
   // es6的写法,返回一个数组(你可以改回es5的写法觉得不习惯的话),第一个是错误信息,第二个是then的异步返回数据,这里要注意一下重复变量声明可能导致问题(这里举例是全局,如果用let,const,请换变量名)。
  [err, result] = await to(setDelay(1000)) 
   // 如果err存在就是有错,不想继续执行就抛出错误
  if (err) throw new Error('出现错误,同时我不想执行了');
  console.log(result);
  [err, result1] = await to(setDelaySecond(12))
   // 还想执行就不要抛出错误
  if (err) console.log('出现错误,同时我想继续执行', err);
  console.log(result1);
  console.log(await setDelay(1000));
  console.log('完成了');
})()

6. async/await的中断(终止程序)

首先我们要明确的是,Promise本身是无法中止的Promise****本身只是一个状态机,存储三个状态(pending,resolved,rejected),一旦发出请求了,必须闭环,无法取消,之前处于pending状态只是一个挂起请求的状态,并不是取消,一般不会让这种情况发生,只是用来临时中止链式的进行。

中断(终止)的本质在链式中只是挂起,并不是本质的取消Promise请求,那样是做不到的,Promise也没有cancel的状态。

不同于Promise的链式写法,写在async/await中想要中断程序就很简单了,因为语义化非常明显,其实就和一般的function写法一样,想要中断的时候,直接return一个值就行,null,空,false都是可以的。看例子:

let count = 6;
const demo = async ()=>{
  const result = await setDelay(1000);
  console.log(result);
  const result1 = await setDelaySecond(count);
  console.log(result1);
  if (count > 5) {
      return '我退出了,下面的不进行了';
    // return; 
    // return false; // 这些写法都可以
    // return null;
  }
  console.log(await setDelay(1000));
  console.log('完成了');
};
demo().then(result=>{
  console.log(result);
})
.catch(err=>{
  console.log(err);
})

实质就是直接return返回了一个Promise,相当于return Promise.resolve('我退出了下面不进行了'),当然你也可以返回一个“拒绝”:return Promise.reject(new Error('拒绝'))那么就会进到错误信息里去。

async函数实质就是返回一个Promise

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值