专栏名称: 前端大全
分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯
目录
相关文章推荐
前端早读课  ·  【第3455期】快手主站前端工程化探索:Gu ... ·  17 小时前  
歸藏的AI工具箱  ·  终于有给设计师用的 Cursor 了 ·  昨天  
歸藏的AI工具箱  ·  终于有给设计师用的 Cursor 了 ·  昨天  
前端早读课  ·  【第3454期】如何用语音学习编程的 ·  昨天  
前端大全  ·  前端行情变了,差别真的挺大。。。 ·  2 天前  
前端早读课  ·  【开源】TinyEngine开启新篇章,服务 ... ·  2 天前  
51好读  ›  专栏  ›  前端大全

async / await:更好的异步解决方案

前端大全  · 公众号  · 前端  · 2017-07-31 21:27

正文

(点击 上方公众号 ,可快速关注)


作者:波同学

www.jianshu.com/p/263d54869a6f

如有好文章投稿,请点击 → 这里了解详情


在实际开发中总会遇到许多异步的问题,最常见的场景接口请求之后一定要等一段时间才能得到结果,如果遇到多个接口前后依赖,那么问题就变得复杂。大家都一直在尝试使用更好的方案来解决这些问题。最开始只能利用回调函数,后来开始有人使用Promise的思维来搞定。到ES6中开始支持原生的Promise,引入Generator函数。


直到ES7,有了async/await。


这是一个用同步的思维来解决异步问题的方案。


我想很多人可能还不太分得清同步与异步的区别。如果你已经彻底了解了事件循环,那么想必对异步的概念应该非常了解。当我们发出了请求,并不会等待响应结果,而是会继续执行后面的代码,响应结果的处理在之后的事件循环中解决。那么同步的意思,就是等结果出来之后,代码才会继续往下执行。


我们可以用一个两人问答的场景来比喻异步与同步。A向B问了一个问题之后,不等待B的回答,接着问下一个问题,这是异步。A向B问了一个问题之后,然后就笑呵呵的等着B回答,B回答了之后他才会接着问下一个问题。


那么我们先记住这个特点,async/await使用同步的思维,来解决异步的问题。在继续讲解它的语法与使用之前,我们先介绍一下如何在我们的开发环境中支持该语法。


如果你已经知道如何配置,可跳过


一、如何在自己的开发环境中支持async/await语法


这里主要介绍两种方式。


1. webpack中支持该语法


首先在当前项目中使用npm下载babel-loader。


> npm install babel-loader --save-dev


然后在配置文件webpack.confing.dev.js中配置,在module.exports.module.rules中添加如下配置元素即可。


{

test : / \.( js | jsx )$ / ,

include : paths . appSrc ,

loader : require . resolve ( 'babel-loader' ),

options : {

cacheDirectory : true ,

},

},


如果你使用最新版本的create-react-app或者vue-cli来构建你的代码,那么它们应该已经支持了该配置。


2. gulp中支持该语法


首先安装gulp插件


> npm install gulp - babel -- save - dev


然后编写任务


var gulp = require ( 'gulp' );

var babel = require ( 'gulp-babel' );

gulp . task ( 'babel' , function () {

return gulp . src ( 'src/app.js' )

. pipe ( babel ())

. pipe ( gulp . dest ( 'dist' ));

});


二、如何使用


async函数是Generator的一个语法糖。如果你不知道Generator是什么函数也没有关系,我们只需要知道async函数实际上返回的是一个Promise对象即可。


async function fn () {

return 30 ;

}

// 或者

const fn = async () => {

return 30 ;

}


在声明函数时,前面加上关键字async,这就是async的用法。当我们用console.log打印出上面声明的函数fn,我们可以看到如下结果:


console . log ( fn ( ) ) ;

// result

Promise = {

__proto__ : Promise ,

[[ PromiseStatus ]] : "resolved" ,

[[ PromiseValue ]] : 30

}


很显然,fn的运行结果其实就是一个Promise对象。因此我们也可以使用then来处理后续逻辑。


fn (). then ( res => {

c onsole . log ( res ); // 30

})


await的含义为等待。意思就是代码需要等待await后面的函数运行完并且有了返回结果之后,才继续执行下面的代码。这正是同步的效果。


但是我们需要注意的是,await关键字只能在async函数中使用。并且await后面的函数运行后必须返回一个Promise对象才能实现同步的效果。


当我们使用一个变量去接收await的返回值时,该返回值为Promise中resolve出来的值。


// 定义一个返回Promise对象的函数

function fn () {

return new Promise (( resolve , reject ) => {

setTimeout (() => {

resolve ( 30 );

}, 1000 );

})

}

// 然后利用async/await来完成代码

const foo = async () => {

const t = await fn ();

console . log ( t );

console . log ( 'next code' );

}

foo ();

// result:

// 30

// next code


运行这个例子我们可以看出,当在async函数中,运行遇到await时,就会等待await后面的函数运行完毕,而不会直接执行next code。


如果我们直接使用then方法的话,想要达到同样的结果,就不得不把后续的逻辑写在then方法中。


const foo = () => {

return fn (). then ( t => {

console . log ( t );

console . log ( 'next code' );

})

}

foo ();


很显然如果使用async/await的话,代码结构会更加简洁,逻辑也更加清晰。


异常处理


在Promise中,我们知道是通过catch的方式来捕获异常。而当我们使用async时,则通过try/catch来捕获异常。


function fn () {

return new Promise (( resolve , reject ) => {

setTimeout (() => {

reject ( 'some error.' );

}, 1000 );

})

}

const foo = async () => {

try {

await fn ();

} catch ( e ) {

console . log ( e ); // some error

}

}

foo ();


如果有多个await函数,那么只会返回第一个捕获到的异常。


function fn1 () {

return new Promise (( resolve , reject ) => {

setTimeout (() => {

reject ( 'some error fn1.' );

}, 1000 );

})

}

function fn2 ()







请到「今天看啥」查看全文