callback
在没有 Promise 之前的时代,在 JS 中写 callback 简直是一种痛苦的煎熬
比如我们要下载一个文件,下载完成之后需要在成功的回调函数里去读写文件
抽象的代码如下:
function processData(url) {
downloadFile({
//... networking
success: function(data) {
readFile(data, function(err, file) {
if (!err) {
file.write(function(data) {
//... write file
});
}
});
},
});
}
这样的代码我们称作 "callback hell"
,回调函数可以是无尽的深渊,那么维护起来会相当困难,简直不要太考验工程师的眼力
Promise
那么 ES2015 引入了 Promise 之后,回调函数的写法有了挺大的改善
我们可以利用 Promise 对上面的代码稍加改造:
function processData(url) {
return new Promise((resolve, reject) => {
downloadFile({
success: resolve,
});
});
}
processData
.then(data => {
return new Promsie((resolve, reject) => {
readFile(data, (err, file) => {
if (err) {
reject(err);
} else {
resolve(file);
}
});
});
})
.then(file => {
file.write(function(data) {
//... write file
});
})
.catch(err => {
console.error(err.message);
});
这样我们使得代码的可读性变得更强了,也更好维护了,但是依然避免不了要在 Promise 中写回调函数的问题
async/await
这时候 ES2017 又推出了更好的解决方案,那就是 Async Function
利用 async function
可以定义一个异步的函数,返回一个 AsyncFunction Object
async function fn() {}
Object.prototype.toString.call(fn); // "[object AsyncFunction]"
当 async function
被调用的时候,它会返回一个 Promise,这个 Promise 会根据函数体内的返回值来 resolve,或者是根据函数体内抛出的异常来 reject
let result = fn();
console.log(r); // Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: undefined}
当函数体为空时,return 值自然是 undefined 所以最终 fn
返回给我们的是一个以 undefined
来 resolve 的 Promise
如果函数体内有 await
表达式的时候,那么 async function
会停止执行并等待 await 的表达式的执行结果以确定最终 Promise 用来 resolve 的值
MDN 上的注解是这么说的:
The purpose of async/await functions is to simplify the behavior of using promises synchronously and to perform some behavior on a group of Promises. Just as Promises are similar to structured callbacks, async/await is similar to combining generators and promises.
重点在于 async/await
简化了我们同步地使用 Promise 来产生基于一组 Promise 的行为,这句话有点难理解,我们来看看代码:
function resolveAfter2s(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function add(x) {
const a = await resolveAfter2s(20);
const b = await resolveAfter2s(30);
return x + a + b;
}
add(10).then(v => {
console.log(v); // prints 60 after 4 seconds.
});
这样就很容易看懂了,我们的 add
函数看起来是同步的,但是其实是利用了 promise 而实现了异步
总结
那么有了 async/await
之后,我们可以更好地分隔我们的代码,使得代码耦合度更低,逻辑更清晰