ES6异步实现的三种方式:Callback/Promise/Async&Await
1.利用callback函数
回调函数,可以理解成作为参数传递的函数对象。
什么是callback函数
下面举个使用回调函数的栗子:
$("#myDiv").on("click", function () {
this.innerHTML = "you clicked me!";
});
这个栗子中,我们使用 jQuery 给一个 id 为 myDiv 的元素绑定了 click 事件的处理程序,这里的事件处理程序就是一个回调函数。当然回调函数并非是一种特殊类型的对象,其实就是普通的函数,但是被作为其他函数的参数传递,在某个时刻才会被选择性地使用。
实际上,这是由 jQuery 提供的机制,在事件处理程序被调用时,this 会指向事件的目标对象,这里也就是被点击的 myDiv 元素,而 HTML 元素有 innerHTML 属性,就是上面的使用方式了。这是怎么实现的呢,合理揣测下的话,肯定是这个回调函数在调用是被显式指定了“上下文对象” this,有可能就是通过 .call() 或 .apply() 的方式。
事实上,这个代码是同步的。如果在JQuery的年代想要实现异步操作需要借助ajax来实现。
通过callback函数来实现异步的栗子:
function getCustomer(id, callback) {
setTimeout(() => {
callback({
id: 1,
name: 'Mosh Hamedani',
isGold: true,
email: 'email'
});
}, 4000);
}
总结:由于写法需要一层层嵌套,容易引起callback hell问题,因此,在处理异步调用时不建议使用此方法。
2.利用promise对象来实现
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise对象有以下两个特点。
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
将上面的栗子代码改成利用promise对象实现如下:
function getCustomer(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id: 1,
name: 'Mosh Hamedani',
isGold: true,
email: 'email'
});
}, 4000);
});
}
总结:缺点:不能用try/catch
3.通过async&await语法糖
在主体函数之前使用了async关键字。在函数体内,使用了await关键字。当然await关键字只能出现在用async声明的函数体内。该函数会隐式地返回一个Promise对象,函数体内的return值,将会作为这个Promise对象resolve时的参数。
可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
// before
getCustomer(1, (customer) => {
console.log('Customer: ', customer);
if (customer.isGold) {
getTopMovies((movies) => {
console.log('Top movies: ', movies);
sendEmail(customer.email, movies, () => {
console.log('Email sent...')
});
});
}
});
// after
async function notifyCustomer() {
const customer = await getCustomer(1);
console.log('Customer: ', customer);
if (customer.isGold) {
const movies = await getTopMovies();
console.log('Top movies: ', movies);
await sendEmail(customer.email, movies);
console.log('Email sent...');
}
}
notifyCustomer();
function getCustomer(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id: 1,
name: 'Mosh Hamedani',
isGold: true,
email: 'email'
});
}, 4000);
});
}
function getTopMovies() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(['movie1', 'movie2']);
}, 4000);
});
}
function sendEmail(email, movies) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 4000);
});
}
总结:
简约而干净Concise and clean
我们看一下上面两处代码的代码量,就可以直观地看出使用Async/await对于代码量的节省是很明显的。对比Promise,我们不需要书写.then,不需要新建一个匿名函数处理响应,也不需要再把数据赋值给一个我们其实并不需要的变量。同样,我们避免了耦合的出现。这些看似很小的优势其实是很直观的,在下面的代码示例中,将会更加放大。
错误处理Error handling
Async/await使得处理同步+异步错误成为了现实。我们同样使用try/catch结构,但是在promises的情况下,try/catch难以处理在JSON.parse过程中的问题,原因是这个错误发生在Promise内部。想要处理这种情况下的错误,我们只能再嵌套一层try/catch。
Async/await是近些年来JavaScript最具革命性的新特性之一,提供了代替Promise的方案。
共同学习,写下你的评论
评论加载中...
作者其他优质文章