Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async就要来了,来不及解释了,快上车! #14

Open
brunoyang opened this issue Mar 18, 2016 · 6 comments
Open

Async就要来了,来不及解释了,快上车! #14

brunoyang opened this issue Mar 18, 2016 · 6 comments

Comments

@brunoyang
Copy link
Owner

brunoyang commented Mar 18, 2016

据说,async 将在2016年 Q2,也就是今年的 4~6 月份于 V8 上推出,到那时,我们就可以欢快地写上『同步代码』啦!所以,本文的目的就是教大家如何使用 async。


配置 webpack + babel 使用 async

如果已配置过,请跳过本节

首先,安装babel-corebabel-loader,这两项分别是 babel 的核心和 babel 能够在 webpack 中运行的保障。

npm install babel-core babel-loader --save-dev

接着,安装babel-preset-stage-3插件,就可以直接使用 async 了。

npm install babel-preset-stage-3 --save-dev

若你希望你的代码可以在浏览器,或是 node v4 以下的环境上运行,就需要加上babel-preset-es2015,因为 babel 转码 async 的原理是将其转为 generator,低版本浏览器若要使用 generator,还需要再转成 es5 的代码。其外,若要在 react 中使用,需另加babel-preset-react

附一份 webpack.config.js 的 loader部分

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

正文

我们先来写一个最简单的 async 函数

function getDataFromGithub() {
  return fetch('https://api.github.com/whatever')
    .then((data) => {
      return data.status;
    });
}

async function githubPage() {
  const data = await getDataFromGithub();
  console.log(data); // 200
  return data;
}

async function wrap() {
  const data = await githubPage();
  console.log(data); // 200
}

wrap();

done!

只需要在 function 前面加上 async 关键字,以及在被调用的函数前面加上 await 关键字,就是这么简单。
来讲讲需要注意的几点

1.首先,请谨记,async的基础是 promise,我们可以试着改写githubPage这个函数

function githubPage() {
  getDataFromGithub()
    .then((v) => {
      console.log(v);
    });
}

对比两个版本的githubPage函数,就能发现,await 相当于是调用了 then 方法,并拿到返回值(当然这只是打个比方,实际上 then 函数是用来注册回调的)。


2.await 关键字不允许出现在普通函数中。map((x) => await x * x),类似这样的代码是会报错的。map(async (x) => await x * x),这样的代码不会报错,但是不符合我们的预期,其中的 await 不是顺序执行而是并行地执行,至于原因,请看这里。没有办法,目前看来,只能在 for 循环中使用

async function foo() {
  const arr = [bar, baz];
  for (const func of arr) {
    await func();
  }
}

async 函数的返回值是一个 promise,也就是说,我们可以写这样的代码:

async function foo() {
  return await getJSON();
}

foo.then((json) => {
  console.log(json);
});

若 await 后的函数 reject 了,或是抛出了一个错,上面的代码是没有办法处理的。当然应对方法也很简单,就是把业务代码都包裹在 try/catch 中,在 catch 中对错误进行统一处理。

async function wrap() {
  try {
    const data = await githubPage();
    console.log(data); // 200
  } catch (e) {
    // ...handle error
  }
}

上面的代码都是顺序执行,那怎么让两个 await 同时执行呢

const [foo, bar] = await* [getFoo(), getBar];

很简单吧,不过,上面的写法已经被废弃了,得用下面这个写法:

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

虽然也很好理解,但不得不吐槽这样写实在是太丑了。
当然,有 Promise.all 自然也可以用 Promise.race

const foo = await Promise.race([getFoo(), getBar()]);

async 也可以在 class 中使用:

class Foo {
  constructor() {
    this.index = 0;
  }

  async test(v) {
    console.log('class A');
    return await Promise.resolve(this.index++);
  }
}

class Bar {
  async test(foo) {
    console.log('class B');
    return await foo.test();
  }
}

const foo = new Foo();
const bar = new Bar();

foo.test().then(v => console.log(v));
bar.test(foo).then(v => console.log(v));
// class A
// class B
// class A
// ----- next tick -----
// 0
// 1

若是像上面这样直接调用,并不会得到预期的结果,因为这相当于是不加 await 调用 async 函数。我们需要将函数调用包装一下~~(快去抢注 co-async)~~:

(async function wrapper() {
  await foo.test().then(v => console.log(v));
  // class A
  // 0
  await bar.test(foo).then(v => console.log(v));
  // class B
  // class A
  // 1
})()
@xudafeng
Copy link

👍

1 similar comment
@superRaytin
Copy link

👍

@guotie
Copy link

guotie commented Apr 23, 2016

async的本质是generator,babel是把async编译成generator的

@puncha
Copy link

puncha commented May 10, 2016

赞!不知道async能不能用在成员函数上?

class AsyncTest {
   async foo() {...}
}

let asyncTest = new AsyncTest();
asyncTest.foo();
`

@brunoyang
Copy link
Owner Author

@puncha 👍 多谢提醒,已在结尾补充

@dengnan123
Copy link

写的很不错,现在用阿里的egg2 可以用原生async很爽

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants