ES8 - async 和 await

async 是“异步”的简写,而 await 可以认为是 async wait 的简写。
所以应该很好理解 async 用于声明一个 函数 是异步的,而 await 用于等待一个异步函数执行完成

一、为什么要引入async

内容转载自:https://blog.csdn.net/zuggs_/article/details/80650436

众所周知,JavaScript语言的执行环境是“单线程”的,那么异步编程对JavaScript语言来说就显得尤为重要。以前我们大多数的做法是使用回调函数来实现JavaScript语言的异步编程。回调函数本身没有问题,但如果出现多个回调函数嵌套,例如:进入某个页面,需要先登录,拿到用户信息之后,调取用户商品信息,代码如下:

this.$http.jsonp('/login', (res) => {
  this.$http.jsonp('/getInfo', (info) => {
    // do something
  })
})

假如上面还有更多的请求操作,就会出现多重嵌套。代码很快就会乱成一团,这种情况就被称为“回调函数地狱”(callback hell)。

于是,我们提出了Promise,它将回调函数的嵌套,改成了链式调用。写法如下:

var promise = new Promise((resolve, reject) => {
  this.login(resolve)
})
.then(() => this.getInfo())
.catch(() => { console.log("Error") })

从上面可以看出,Promise的写法只是回调函数的改进,使用then方法,只是让异步任务的两段执行更清楚而已。Promise的最大问题是代码冗余,请求任务多时,一堆的then,也使得原来的语义变得很不清楚。此时我们引入了另外一种异步编程的机制:Generator。
Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号*;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。一个简单的例子用来说明它的用法:

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator(); 

上面代码定义了一个 Generator 函数helloWorldGenerator,它内部有两个yield表达式(hello和world),即该函数有三个状态:hello,world 和 return 语句(结束执行)。Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。上述代码分步执行如下:

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true } 

Generator函数的机制更符合我们理解的异步编程思想。

用户登录的例子,我们用Generator来写,如下:

var gen = function* () {
  const f1 = yield this.login()
  const f2 = yield this.getInfo()
};

虽然Generator将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。此时,我们便希望能出现一种能自动执行Generator函数的方法。我们的主角来了:async/await。

ES8引入了async函数,使得异步操作变得更加方便。简单说来,它就是Generator函数的语法糖。

async function asyncFunc(params) {
  const result1 = await this.login()
  const result2 = await this.getInfo()
}

是不是更加简洁易懂呢?

二、async的用法具体解析

在函数前面加上async 关键字,来表示它是异步的,那怎么调用呢?async 函数也是函数,平时我们怎么使用函数就怎么使用它,直接加括号调用就可以了。

async function timeout() {
    return 'hello world'
}
console.log(timeout());  //打印async函数执行后是什么值,这里的输出并不是它真的执行内部代码了
timeout().then( (data) => {
	console.log(data);
});
console.log('虽然在后面,但是我先执行');

输出结果:
Promise {: “hello world”}
虽然在后面,但是我先执行
hello world

async 函数返回的是一个Promise 对象,如果要获取到Promise 返回值,我们应该用then 方法或者catch方法(当Promise对象返回resolved状态之则调用then方法;async 函数内部抛出错误返回rejected状态则调用catch方法)。

async function timeout(flag) {
    if (flag) {
        return 'hello world'
    } else {
        throw 'my god, failure'
    }
} 

timeout(true).then(data => {
    console.log(data)
})
timeout(false).catch(err => {
    console.log(err)
})

输出结果:
hello world
my god, failure

await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的

function getSomething() {
    return "normal function";
}
async function testAsync() {
    return Promise.resolve("async function");
}

async function test() {
    const v1 = await getSomething(); // await后面跟普通函数
    const v2 = await testAsync();    // await后面跟async函数
    console.log(v1);
    console.log(v2);
}

test();
//最后输出:
// normal function
// async function

如果await等到的是一个 Promise 对象,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。await 就忙起来了,它会阻塞后面的代码。这里的阻塞——就是 await 必须用在 async 函数中的原因。async 函数调用不会造成阻塞,它内部所有的阻塞都被封装在一个 Promise 对象中异步执行。

// 2s 之后返回双倍的值
function doubleAfter2seconds(num) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2 * num)
        }, 2000);
    } )
    // return Promise.resolve(2 * num);
}

async function testResult() {
    let result = await doubleAfter2seconds(30);
    console.log(result);
}
testResult();  // 2s之后,控制台输出 60

————————————————————————————————————
使用 ES7 的 async/await 时报错现象:
原因:这个regeneratorRuntime在浏览器上是不认识的,通过百度,需要安装一个babel-plugin-transform-runtime插件。
1、使用npm安装
npm i --save-dev babel-plugin-transform-runtime
2、然后在 .babelrc 文件中添加:

{
"plugins": [
	[
	    "transform-runtime",
	    {
	      "helpers": false,
	      "polyfill": false,
	      "regenerator": true,
	      "moduleName": "babel-runtime"
	    }
    ]
  ]
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值