一节课完全弄懂promise、async、await(三)完结篇

以前聊了异步编程的回调函数和promise,上一篇文章也说了,用promise解决异步编程,若是多个调用,就会看起来不那么舒服。ios

es6除了提供了promise还为咱们提供了更增强大的async和await,async、await是Generator函数的语法糖,若是想要彻底掌握async、await的用法,必需要掌握Generator函数的使用。es6

1、Generator 函数

一、什么是 Generator 函数?

来自阮一峰老师文档上的解释:Generator函数是协程在 ES6 的实现,最大特色就是能够交出函数的执行权(即暂停执行)。面试

你能够这么理解,这个函数本身执行不了,得让别人帮忙执行,踢一脚(next()),走一步。编程

基本的用法:

function* doSomething() {
    yield '吃饭'
    return '睡觉'
}

let newDoSomething = doSomething() // 本身执行不了,须要指向一个状态机

console.log(newDoSomething.next()) // {value: "吃饭", done: false}
console.log(newDoSomething.next()) // {value: "睡觉", done: true}
复制代码
从上面的例子能够看出来,Generator 函数有四个特色:

一、function后面有个小*,这个地方有两种写法,没啥太大区别;axios

function* doSomething(){}
function *doSomething(){}
复制代码

二、函数里面会有一个yield,把函数截成不一样的状态;promise

一个yield能够截成两个状态,也就须要两个next()触发;
复制代码

三、Generator函数本身不会执行,而是会返回一个遍历器对象;微信

四、遍历器对象会经过.next()方法依次调用各个状态。markdown

消息传递

Generator函数除了能控制函数分状态的执行,还有一个很是重要的做用就是消息传递,仍是上例子:异步

function *doSomething() {
    let x = yield 'hhh'
    console.log(x)
    return (x * 2)
}

let newDoSomething = doSomething()

console.log(newDoSomething.next(1))  
console.log(newDoSomething.next(2))  


打印结果:

{value: "hhh", done: false}
2
{value: 4, done: true}
复制代码

具体分析一下为何会打印这个: (重点async

//{value: "hhh", done: false}
第一个next()是Generator函数的启动器
这个时候打印的是yield后面的值
重点的一句,yield后面的值并不会赋值给x

//2
暂停执行的时候,yield表达式处能够接收下一个启动它的next(...)传进来的值
你能够理解为:
这个时候第二个next传入的参数会把第一个yield的值替换掉

 //{value: 4, done: true}
这个时候,x被赋值2,因此打印2*2
复制代码
注意几个问题:
第一个next()是用来启动Generator函数的,传值会被忽略,没用
yield的用法和return比较像,你能够当作return来用,若是yield后没值,return undefined
最后一个next()函数,获得的是函数return的值,若是没有,也是undefined
完全理解了上面的概念,再来看下下面的栗子:
function *doSomething() {
    let x = yield 'hhh'
    let y = yield (x + 3)
    let z = yield (y * 3)
    return (x * 2)
}

let newDoSomething = doSomething()

console.log(newDoSomething.next(1))  // {value: "hhh", done: false}
console.log(newDoSomething.next(2))  // {value: 5, done: false}
console.log(newDoSomething.next(100)) // {value: 300, done: false}
console.log(newDoSomething.next(1000)) // {value: 4, done: true}
复制代码

仍是用上面的思路分析一下:

第一个next(1),传进去的值没用,打印的是yield后的值
第二个next(2),这个时候的x已经被赋值为2,因此打印2+3
第三个next(100),这个时候的y已经被赋值为100,因此打印100*3
第四个next(1000),这个时候y已经被赋值为1000,,可是打印的是x*2,因此打印的4 
复制代码
再来看个特殊的状况:(特殊的才是容易掉坑的)
function *doSomething() {
    let x = yield 'hhh'
    console.log(x)
    let y = yield (x + 3)
    console.log(y)
    let z = yield (y * 3)
    return (x * 2)
}

let newDoSomething = doSomething()

console.log(newDoSomething.next(1))
console.log(newDoSomething.next(2))
console.log(newDoSomething.next())
console.log(newDoSomething.next())
复制代码

看下打印结果:

{value: "hhh", done: false}
2
{value: 5, done: false}
undefined
{value: NaN, done: false}
{value: 4, done: true}
复制代码

分析下为何打印undefined?

一、第一个next(1)传进去的1,没有起任何意义,打印的{value: "hhh", done: false};
二、第二个next(2)传进去的2,因此x会打印2,第二个next(2)打印2+3;
三、第三个next()传进去的是空,那么y打印的就是未定义,undefined*3那确定就是NaN;
四、第四个next()传进去的是空,可是return的是x,刚才说了x是2,那打印的是2*2
复制代码

2、async、await

一、什么是async、await?

async、await是Generator函数的语法糖,原理是经过Generator函数加自动执行器来实现的,这就使得async、await跟普通函数同样了,不用再一直next执行了。

他吸取了Generator函数的优势,能够经过await来把函数分状态执行,可是又不用一直next,能够自动执行。

仍是上例子:

栗子1
function f() {
    return new Promise(resolve =>{
        resolve('hhh')
    })
}
async function doSomething1(){
    let x = await f()
}

doSomething1()

打印结果:

hhh
复制代码

看了上面的例子,能够看出async有三个特色:

一、函数前面会加一个async修饰符,来证实这个函数是一个异步函数;

二、await 是个运算符,用于组成表达式,它会阻塞后面的代码

三、await 若是等到的是 Promise 对象,则获得其 resolve值。
复制代码
栗子2
async function doSomething1(){
    let x = await 'hhh'
    return x
}

console.log(doSomething1())

doSomething1().then(res => {
    console.log(res)
})

打印结果:

Promise {<pending>}
hhh
复制代码

分析一下上面的栗子能够获得这两个特色:

一、async返回的是一个promise对象,函数内部 return 返回的值,会成为 then 方法回调函数的参数;

二、await若是等到的不是promise对象,就获得一个表达式的运算结果。
复制代码

async、await项目中的使用

如今有一个封装好的,获取数据的方法,咱们分别用promise、Generator、async来实现发请求,作一下比较:

function getList() {
    return new Promise((resolve, reject) =>{
        $axios('/pt/getList').then(res => {
            resolve(res)
        }, err => {
            reject(err)
        })
    })
}
复制代码

promise

function initTable() {
    getList().then(res => {
        console.log(res)
    }).catch(err => {
        this.$message(err) // element的语法
    })
}
复制代码

而后直接调用就能够 这么作看起来很是的简洁,可是若是多个请求调用 就会是.then,.then看起来很是不舒服

Generator函数

function *initTable(args) {
    const getList = yield getlist(args)
    return getList
}

function getList() {
    const g = initTable(this.searchParams)
    const gg = g.next().value
    gg.then(res =>{
        this.total = res.data.count
        if (res.data.list) {
          this.tableList = res.data.list
          this.tableList.forEach(e => {
            e.receiveAmt = format(e.receiveAmt)
          })
        } else {
          this.tableList = []
        }
    })
}
复制代码

这个看起来就比较伤,写起来很是麻烦

async await

async initTable() { // table列表查
  const getData = await getList(this.searchParams)
  return getData
},

getList() {
  this.initTable().then(res =>{
    this.tableList = res.data.list
  })
}
复制代码

这样写好像也很简单,并且很是方便

主要是若是调用多个接口,能够直接多个await

总结

由于一个好朋友的一道面试题,忽然有了写一下关于同步、异步编程的一篇文章,写起来才发现涉及的知识点太杂,就一共写了三篇文章:

一、关于什么同步、异步,其中涉及了一些堆栈和消息队列、事件轮询的知识;

mp.weixin.qq.com/s/LAwfxg0TK…

二、关于异步编程的几个解决方案,主要是回调函数和promise;

mp.weixin.qq.com/s/z7b5jzRd1…

三、关于异步编程的终极解决方案Generator函数以及他的语法糖async、await。

我的的微信公众号:小Jerry有话说,平时会发一些技术文章和读书笔记,欢迎交流。

相关文章
相关标签/搜索