ES7 提出的async
函数,终究让 JavaScript 关于异步操纵有了最终处置惩罚方案。No more callback hell。async
函数是 Generator
函数的语法糖。运用 关键字 async
来示意,在函数内部运用 await
来示意异步。
想较于 Generator,Async
函数的革新在于下面四点:
内置实行器。Generator 函数的实行必需依托实行器,而
Aysnc
函数自带实行器,挪用体式格局跟一般函数的挪用一样更好的语义。
async
和await
相较于*
和yield
越发语义化更广的适用性。
co
模块商定,yield
敕令背面只能是 Thunk 函数或 Promise对象。而async
函数的await
敕令背面则可所以 Promise 或许 原始范例的值(Number,string,boolean,但这时刻等同于同步操纵)返回值是 Promise。
async
函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象轻易,能够直接运用then()
要领举行挪用
Async 与其他异步操纵的对照
先定义一个 Fetch 要领用于猎取 github user 的信息:
function fetchUser() {
return new Promise((resolve, reject) => {
fetch('https://api.github.com/users/superman66')
.then((data) => {
resolve(data.json());
}, (error) => {
reject(error);
})
});
}
Promise 体式格局
/**
* Promise 体式格局
*/
function getUserByPromise() {
fetchUser()
.then((data) => {
console.log(data);
}, (error) => {
console.log(error);
})
}
getUserByPromise();
Promise 的体式格局虽然处置惩罚了 callback hell,然则这类体式格局充溢了 Promise的 then()
要领,假如处置惩罚流程庞杂的话,整段代码将充溢 then
。语义化不显著,代码流程不能很好的示意实行流程。
Generator 体式格局
/**
* Generator 体式格局
*/
function* fetchUserByGenerator() {
const user = yield fetchUser();
return user;
}
const g = fetchUserByGenerator();
const result = g.next().value;
result.then((v) => {
console.log(v);
}, (error) => {
console.log(error);
})
Generator 的体式格局处置惩罚了 Promise 的一些题目,流程越发直观、语义化。然则 Generator 的题目在于,函数的实行须要依托实行器,每次都须要经由过程 g.next()
的体式格局去实行。
async 体式格局
/**
* async 体式格局
*/
async function getUserByAsync(){
let user = await fetchUser();
return user;
}
getUserByAsync()
.then(v => console.log(v));
async
函数圆满的处置惩罚了上面两种体式格局的题目。流程清楚,直观、语义显著。操纵异步流程就犹如操纵同步流程。同时 async
函数自带实行器,实行的时刻无需手动加载。
语法
async 函数返回一个 Promise 对象
async
函数内部 return 返回的值。会成为 then
要领回调函数的参数。
async function f() {
return 'hello world'
};
f().then( (v) => console.log(v)) // hello world
假如 async
函数内部抛出非常,则会致使返回的 Promise 对象状况变成 reject
状况。抛出的毛病而会被 catch
要领回调函数接收到。
async function e(){
throw new Error('error');
}
e().then(v => console.log(v))
.catch( e => console.log(e));
async 函数返回的 Promise 对象,必需比及内部一切的 await 敕令的 Promise 对象实行完,才会发作状况转变
也就是说,只有当 async
函数内部的异步操纵都实行完,才会实行 then
要领的回调。
const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout));
async function f(){
await delay(1000);
await delay(2000);
await delay(3000);
return 'done';
}
f().then(v => console.log(v)); // 守候6s后才输出 'done'
一般情况下,await 敕令背面随着的是 Promise ,假如不是的话,也会被转换成一个 马上 resolve 的 Promise
如下面这个例子:
async function f() {
return await 1
};
f().then( (v) => console.log(v)) // 1
假如返回的是 reject 的状况,则会被 catch
要领捕捉。
Async 函数的毛病处置惩罚
async
函数的语法不难,难在毛病处置惩罚上。
先来看下面的例子:
let a;
async function f() {
await Promise.reject('error');
a = await 1; // 这段 await 并没有实行
}
f().then(v => console.log(a));
如上面所示,当 async
函数中只需一个 await
涌现 reject 状况,则背面的 await
都不会被实行。
处置惩罚办法:能够增加 try/catch
。
// 准确的写法
let a;
async function correct() {
try {
await Promise.reject('error')
} catch (error) {
console.log(error);
}
a = await 1;
return a;
}
correct().then(v => console.log(a)); // 1
假如有多个 await
则能够将其都放在 try/catch
中。
如安在项目中运用
依然是经由过程 babel
来运用。
只须要设置 presets
为 stage-3
即可。
装置依靠:
npm install babel-preset-es2015 babel-preset-stage-3 babel-runtime babel-plugin-transform-runtime
修正.babelrc
:
"presets": ["es2015", "stage-3"],
"plugins": ["transform-runtime"]
如许就能够在项目中运用 async
函数了。
Further Reading
文章首发于我的博客:chenhuichao.com