前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >async/await 一种非常丝滑的异步语法

async/await 一种非常丝滑的异步语法

作者头像
villainhr
发布2018-07-03 15:09:33
6370
发布2018-07-03 15:09:33
举报
文章被收录于专栏:前端小吉米

async是es7提出的一种新的异步语法. 一开始es为了解决异步,使用的是promise, 但看到满屏的then之后,就感觉自己傻逼了. 后来提出了generator, 在底层实现了一个异步的模式, 但需要手动执行. 关于如何使用generator,可以参考,how to use generator. 本文这里,不探讨怎么使用generato. 而是,如果使用generator和promise 构造出async的表达. 文末后面会介绍如何正式的使用 async/await,以及里面有什么需要注意的内容。

简单异步实现

一般异步的写法就是,传回调嘛,比如:

代码语言:javascript
复制
var ajax = function(url,cb){
    // send url
    // get res
    ...
    cb(JSON.parse(result))
}

这样,应该最容易写成callback hell. 然后我们引入: generator

代码语言:javascript
复制
function *main() {
    var result1 = yield ajax( "http://some.url.1" );
    var data = JSON.parse( result1 );

    var result2 = yield ajax( "http://some.url.2?id=" + data.id );
    var resp = JSON.parse( result2 );
}

var async = main();
async.next();

function ajax(url){
    // send url
    async.next(res);
}

伪代码的格式如上所述. 这里,需要主意一个点. 如果,你没有在next()中传入res,like async.next(). 那么,里面result1和result2获得的结果就是undefined. 上面就是基本的generator异步. 那如果使用generator来模拟async呢? 这估计得解释一下async出现的原因

async why?

根据上面的解析,我们可以了解到, 使用next 执行语句时, 只能执行yield后面的表达式. 这样造成的结果就是,不能parallel异步呀. 这样限制性还是很大的。所以,为了解决这个问题,使用到es6提出的Promise对象来进行补充. 这里,增加一个限定规则,即,ajax拉取返回的必须是一个promise对象.

代码语言:javascript
复制
function ajax(url){
    return new Promise(function(res,rej){
        send(url,function(result){
            res(result)
        })
    })
}

我们再补充一下,如果使用generator来对promise进行tricky

代码语言:javascript
复制
function runGenerator(g) {
    var it = g(), ret;
    (function iterate(val){
        ret = it.next( val );
        if (!ret.done) {
            // 检查是否已经then完成
            if ("then" in ret.value) {
                // 这一句很关键
                ret.value.then( iterate );
            }
            else {
                // 同步回调的trick
                setTimeout( function(){
                    iterate( ret.value );
                }, 0 );
            }
        }
    })();
}

OK, 这样, 我们就可以在async里面,使用同步的写法,来代表异步的操作了.

代码语言:javascript
复制
runGenerator(function* (){
    var result = yield new Promise(function(res,rej){
        setTimeout(function(){
            res('ok');
        },1000)
    });
})

由于这里要求的是使用promise, 那么,我们使用Promise.all([xx,xx]) 也是合情合理的. 这样就可以完美的解决掉--并行异步发送。

代码语言:javascript
复制
runGenerator(function* (){
    yield Promise.all([ajax('http://villainhr.com'),ajax('http://villainhr.com')])
})

对比与,使用async的结构:

代码语言:javascript
复制
(async function(){
    await Promise.all([ajax('http://villainhr.com'),ajax('http://villainhr.com')]);
})();

是不是感觉一毛一样呢? 不过在实际上操作中, async 还必须对new Promise进行兼容处理. 如果其他人直接传入一个expression, 你也必须保证他是可行的. 在babel中,讲的其实也是这样一个逻辑:

代码语言:javascript
复制
// In

async function foo() {
  await bar();
}
// Out

var _asyncToGenerator = function (fn) {
  ...
};
var foo = _asyncToGenerator(function* () {
  yield bar();
});

具体参考: babel es6 转码

工程实践 async

前面已经基本介绍如何使用 async 这里,简单介绍一下如何在工程中接入 async。这里,我们以 webpack为例,只需要额外下载 stage-3,并修改配置即可。

代码语言:javascript
复制
# 下载 stage-3
npm install babel-preset-stage-3 --save-dev

# 修改 config 配置
module: {
  loaders: [{
    test: /\.jsx?$/,
    include: [
      path.resolve(__dirname, 'src')
    ],
    loader: 'babel-loader',
    query: {
      presets: ['es2015', 'stage-3', 'react'],
    }
  }]
}

不过,上面那种是针对浏览器环境比较好的条件下,一般来说,如果针对一些低版本浏览器,还需要使用 stage-0 的配置,那么写法就应该改成。

代码语言:javascript
复制
entry: ['babel-polyfill', './test.js'],
loaders: [
      { test: /\.jsx?$/, loader: 'babel', exclude:/node_modules/,
      presets: ['es2015','stage-0', 'react'],
      }
    ]

在具体使用 async 时,会遇到各种使用 case,这里我们按照顺序,来简单的描述一下。

基础使用

在使用 await 时,需要注意,其修饰的就是一个 Promise 对象 或者 async 函数,不能修饰非异步对象。并且,使用 await 时,外部块级作用域一定需要使用 async 进行包裹。

代码语言:javascript
复制
function promiseFunc () {
  return fetch('https://api.github.com/whatever')
    .then((data) => {
      return data.status;
    });
}

async function AsyncFunc () {
  const data = await promiseFunc ();
  console.log(data); 
  return data;
}

async function wrap() {
  const data = await AsyncFunc ();
  console.log(data); 
}

reject 错误捕获

使用 async 时,捕获 Promise 中的错误写法有两种,一种是直接使用 try-catch 进行捕获,一种是直接通过 catch() 来捕获。

代码语言:javascript
复制
async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另一种写法

async function myFunction() {
  await somethingThatReturnsAPromise().catch(function (err){
    console.log(err);
  });
}

并行 async

使用并行 async 时,可以直接利用 Promise.all 执行即可:

代码语言:javascript
复制
const [foo, bar] = await Promise.all([getFoo(), getBar()]);

非 async 修饰时调用 await

正常情况下,使用 await 是需要包裹在 async 这样才能利用 generator 来暂停当前块级作用域。否则的话,他会直接按照异步的模式,顺序执行。

代码语言:javascript
复制
async A(v) {
    console.log('class A');
    return await Promise.resolve();
  }

async B(foo) {
    console.log('class B');
    return await Promise.resolve();
  }

A().then(()=>{console.log("1")})
B().then(()=>{console.log("2")})

# 只会输出
A
B
1
2

如果要保证顺序执行的话,则需要使用 async 进行包裹。

代码语言:javascript
复制
(asycn function test(){
    await A();
    console.log("1")
    await B();
    console.log("2")
})()

常用技巧

检测浏览器支持 async

有时候需要了解一个浏览器是否支持 async ,然后针对不同的异步写法来做相关的兼容。这时,异步检测特性就显得极为重要。那如何快速用同步的方式在浏览器里面检测是否支持 async,可以直接通过 newFunction(xxx) 的方式来做。直接看代码吧:

代码语言:javascript
复制
function asyncDetect(){
    try {
        new Function('async function test() {}')();
      } catch (e) {
        return false;
      }
      return true;
}

如果你需要检测是否支持 generator 的话,可以直接使用:

代码语言:javascript
复制
function generatorDetect (){
    try {
        new Function('function* test() {}')();
      } catch (e) {
        return false;
      }
      return true;
}

参考

async/await 使用

async rejected 的使用

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端小吉米 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简单异步实现
  • async why?
  • 工程实践 async
    • 基础使用
      • reject 错误捕获
        • 并行 async
          • 非 async 修饰时调用 await
          • 常用技巧
            • 检测浏览器支持 async
            • 参考
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档