Generator和async

什么是Generator函数

Generator函数与普通函数的区别是该函数可以分步骤阻塞,不像普通函数需要一路走到底,就像是Generator生成一堆的小函数,只有主动调用next()才会一个个的执行这些小函数。总结起来就是Generator函数中间可以停下来,可以使用yield来暂时的放弃执行。

拿一个形象的例子:普通函数好比是坐高铁或者乘坐飞机,我们只有到达目的地了,才停止下来,中间是不允许有停歇的,但是Generator函数好比是乘坐计程车,当我们需要在某个地方去做什么事,比如上个厕所,是可以叫师傅在某地方等一会,回来了再继续赶往目的地

yield的理解与使用

Generator是一个状态机,封装了多个内部状态,执行 Generator 函数会返回一个遍历器对象,返回的遍历器对象,可以使用next依次遍历Generator函数内部的每一个状态。Generator函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。Generator 函数的执行必须靠执行器。

yield可以传参也可以返回内容(一般的返回的是上一步执行的结果)。一个yield就是一堵墙,墙的两面都是小的函数,只有墙的一面的函数执行完了,才可以接着执行另一面的函数。如下图帮助理解:
在这里插入图片描述

yield传参的形式与意义,如下图:
在这里插入图片描述
Generatoryield综合使用,如下伪代码:
在这里插入图片描述

Generator相比Promise的优势

Promise也是处理异步的,但为什么会再出现generator,是因为当我们使用promise的时候,如果中间需要有一些逻辑的判断,则这样写出来的promise代码相比普通的函数而言是没有任何的优势的。但是如果使用generator可以简化很多,并且是有序,正确的执行 每道工序。

总结:promise适合处理一次读一堆的异步操作,而generator适合在读的过程中有一些逻辑的处理,分批,有顺序的处理。简单的来说是generator是对promise的封装

async函数

async函数是Generator函数的语法糖,将Generator的星号换成asyncyield换成awaitasync函数比Generator函数更好用

  • 自带执行器,执行起来,跟调用普通函数一样
  • asyncawait 语义更清晰,async表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果
  • await后面啥都可以跟,可以是Promise 也可以是对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作
  • async函数的 返回值是 Promise
    正常情况下,await命令是个Promise对象,如果不是 会被转成一个 立即resolved的对象,async函数完全可以看作多个异步操作,包装成的一个Promise对象(因为await函数返回的是Promise对象),而await命令就是内部then命令的语法糖。
    然而,然而,我们没写错误处理。
async function f() {
  return 'hello world';
}
f().then().catch()

正常情况下 async 函数中return结果会使Promise对象变为 resolved状态,返回值作为then方法回调函数的参数,而出错则会使Promise对象的变为reject状态,错误会被catch捕获。

因为 async函数 相当于对 多个Promise的封装,所以必须等到内部所有的await命令执行完,才会改变自己的状态为resolved,除非 碰到return语句或者抛出了异常。
也就是说,正常情况下 只有async函数内部的异步操作执行完,才会执行then后面的语句
只要一个await后面的Promise变为rejected,整个async函数就会中断执行,整个async返回的Promise对象就会是rejected状态

async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}

因为第一个await后面的对象reject了,所以整个async函数就中断执行了
有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。
这时可以将第一个await放在try…catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。
await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中。

try catch

try catch是JavaScript的异常处理机制,把可能出错的代码放在try语句块中,如果出错了,就会被catch捕获来处理异常。如果不catch 一旦出错就会造成程序崩溃。

如果有多个await命令,可以将其都放在try catch结构中,如果执行出错,catch会去捕获异常

async function f() {
    try {
     await Promise.reject('出错了');
     console.log('上面已经出错了');
     return await Promise.resolve('hello world');
    } catch(e) {
        console.log(e);
    }
  }
  
  f()
  .then(v => console.log(v))

catch会去捕获try代码块中的错误,只要有一个抛出了异常,就不会继续执行,所以上面的代码不会打印上面已经出错了也不会执行return await Promise.resolve(‘hello world’);
因为使用了trycatch 所以 async 是顺利执行完成的,其中的报错 被 try catch处理了,所以异常不会被async返回的Promise的catch捕获,因此async返回的Promise对象状态是resolved

如果异步函数没有依赖关系,最好并发执行

await 会等待后面的异步操作执行完毕,才会继续执行

let foo = await getFoo();
let bar = await getBar();

上面的代码会顺序执行,
如果需要多个await没有相互依赖,最好让他们同时触发,可以使用以下两种方式:

使用Promise.all() 包装一个新的Promise对象

let [foo, bar] = await Promise.all([getFoo(), getBar()]);

不等待分别执行,返回新的Promise对象
//没用await 立即执行返回 Promise对象

let fooPromise = getFoo();
let barPromise = getBar();

// 等待 Promise对象的结果 之前也说过 await就像是then的语法糖
let foo = await fooPromise;
let bar = await barPromise;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章