ES6+ 现在就用系列(十):Async 异步编程

Async

Async是ES7推出的新关键字,是为了更方便的进行异步编程,虽然我们之前使用Promise来使代码看着更简洁,但是还是有一堆的then, 那么我们能否让异步调用看起来和同步一样呢? 就是看代码从左到右,从上到下的方式。

我们回顾一下,callback 到 promise.

典型的callback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function handler(request, response) {
User.get(request.user, function(err, user) {
if (err) {
response.send(err);
} else {
Notebook.get(user.notebook, function(err, notebook) {
if (err) {
return response.send(err);
} else {
doSomethingAsync(user, notebook, function(err, result) {
if (err) {
response.send(err)
} else {
response.send(result);
}
});
}
});
}
})
}

使用Promise后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function(request, response) {
var user, notebook;

User.get(request.user)
.then(function(aUser) {
user = aUser;
return Notebook.get(user.notebook);
})
.then(function(aNotebook) {
notebook = aNotebook;
return doSomethingAsync(user, notebook);
})
.then(function(result) {
response.send(result)
})
.catch(function(err) {
response.send(err)
})
}

那么,我们如果使用Async 关键字后:

1
2
3
4
5
6
7
8
9
async function(request, response) {
try {
var user = await User.get(request.user);
var notebook = await Notebook.get(user.notebook);
response.send(await doSomethingAsync(user, notebook));
} catch(err) {
response.send(err);
}
}

这个,C#程序员已经很熟悉了,就是 await的关键字会使程序立即返回,等await的代码处理完毕后,再继续执行后面的代码。

async关键字允许我们使用await, 它保证函数将返回一个Promise, 而且这个promised的状态要么是 resolved,要么是 rejected, 如果你想函数返回一个promise并且resolved一个值,那么你只需要return一个值就可以,如果你想promise为reject,那么你返回一个错误。

1
2
3
4
5
6
7
async function run(){
if(Math.round(Math.random())){
return 'Success!';
} else {
throw 'Failure!';
}
}

实际上等于下面的代码

1
2
3
4
5
6
7
function run(){
if(Math.round(Math.random())){
return Promise.resolve('Success!');
}else{
return Promise.reject('Failure!');
}
}

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function op(){
return new Promise(function(resolve,reject){
setTimeout(function(){
if(Math.round(Math.random())){
resolve('Success')
}else{
reject('Fail')
}
},2000)
});
}

async function foo(){
console.log('running')
try {
var message = await op();
console.log(message)
} catch(e) {
console.log('Failed!', e);
}
}

foo()

await 其实 wait 一个promise

1
2
3
4
5
6
var RP = require("request-promise");
var sites = await Promise.all([
RP("http://www.google.com"),
RP("http://www.apple.com"),
RP("http://www.yahoo.com")
])

Async 实战

我们将调用github API 然后取得某一用户的profile和他的repositories.

环境搭建

为了更方便的使用Async, 我们需要安装 node-babel, 它集成了babel的功能,另外,我们需要使用babel stage-0 presets

1
2
3
4
npm install -g node-babel
npm install -g babel-cli
npm install --save-dev babel-preset-es2015
npm install babel-preset-stage-0

然后新建一个目录

1
2
jacks-MacBook-Air:~ jack$ mkdir asyncdemo && cd asyncdemo
jacks-MacBook-Air:asyncdemo jack$

然后新建一个 .babelrc

1
2
acks-MacBook-Air:asyncdemo jack$ touch .babelrc
jacks-MacBook-Air:asyncdemo jack$ code .

在 .babelrc 里写入一下代码

1
2
3
{
"presets": ["es2015","stage-0"]
}

我们在安装一个 node-fetch 库,可以比较方便的调用API.

1
npm i node-fetch

我们创建 script.js, 先用promise的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import fetch from 'node-fetch';

const username="wangdeshui";

function getProfile(){
return fetch(`https://api.github.com/users/${username}`);
}

function getRepos(){
return fetch(`https://api.github.com/users/${username}/repos`);
}

getProfile()
.then((profileResponse)=>profileResponse.json())
.then((profile)=>{
return getRepos()
.then((reposResponse)=>reposResponse.json())
.then((repos)=>{
return {
repos,
profile
};
});
})
.then((combined)=>{

console.log(combined);

})
.catch((err)=>{
console.log(err);
});

现在,我们对调用部分改为 await

1
2
3
4
5
6
7
8
9
10
11
import fetch from 'node-fetch';

const username="wangdeshui";

function getProfile(){
return fetch(`https://api.github.com/users/${username}`);
}

function getRepos(){
return fetch(`https://api.github.com/users/${username}/repos`);
}
1
var profile= await getProfile();

上面代码将报错,因为 await 只能等待async 标记的函数。

我们改成如下这样,还是不行

1
2
3
4
5
6
async function getCombined(){

let profile=await getProfile();
}

await getCombined();

我们改为如下就可以运行

1
2
3
4
5
6
7
8
9
async function getCombined(){

let profile=await getProfile();
}

(async function()
{
await getCombined();
}());

最后,我们把上面的例子完整的改为async

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
async function getCombined(){

let profileResponse=await getProfile();
let profile=await profileResponse.json();
let reposResponse=await getRepos();
let repos= await reposResponse.json();

return {
repos,
profile
};

}

(async function()
{
try
{
let combined= await getCombined();
console.log(combined);
}
catch(err)
{
console.error(err);
}
}());

实际调用的时候,我们一般是这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function getCombinedResults ()
{
try
{
let combined= await getCombined();
console.log(combined);
}
catch(err)
{
console.error(err);
}
};

getCombinedResults();

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function getCombined(){

let profileResponse=await getProfile();
let profile=await profileResponse.json();
let reposResponse=await getRepos();
let repos= await reposResponse.json();

return {
repos,
profile
};

}

getCombined().then((data)=>console.log(data));

Author

王德水

Posted on

2016-01-23

Updated on

2021-01-06

Licensed under

版权:本文版权归作者所有,转载需经作者同意。

Comments