上一章我们了解了co
与Generator结合的异步编程解决方案。
我知道你想说什么,写一个异步调用还得引入一个npm包(虽然是大神TJ写的包)。
妈卖批的npm!
当然是不存在的。如果一个特性足够重要,社区的呼声足够高,它就一定会被纳入标准的。马上我们要介绍的就是血统纯正的异步编程家族终极继承人——爱新觉罗·async。
往期:
import co from 'co';
function fetchByName(name) {
const url = `https://api.github.com/users/${name}/repos`;
return fetch(url).then(res => res.json());
}
co(function *gen() {
const value1 = yield fetchByName('veedrin');
console.log(value1);
const value2 = yield fetchByName('tj');
console.log(value2);
});
function fetchByName(name) {
const url = `https://api.github.com/users/${name}/repos`;
return fetch(url).then(res => res.json());
}
async function fetchData() {
const value1 = await fetchByName('veedrin');
console.log(value1);
const value2 = await fetchByName('tj');
console.log(value2);
}
fetchData();
看看这无缝升级的体验,啧啧。
灵活
别被新的关键字吓到了,它其实非常灵活。
async function noop() {
console.log('Easy, nothing happened.');
}
这家伙能执行吗?当然能,老伙计还是你的老伙计。
async function noop() {
const msg = await 'Easy, nothing happened.';
console.log(msg);
}
同样别慌,还是预期的表现。
只有当await
关键字后面是一个Promise的时候,它才会显现它异步控制的威力,其余时候人畜无害。
function fetchByName(name) {
const url = `https://api.github.com/users/${name}/repos`;
return fetch(url).then(res => res.json());
}
async function fetchData() {
const name = await 'veedrin';
const repos = await fetchByName(name);
console.log(repos);
}
虽然说await
关键字后面跟Promise或者非Promise都可以处理,但对它们的处理方式是不一样的。非Promise表达式直接返回它的值就是了,而Promise表达式则会等待它的状态从pending
变为fulfilled
,然后返回resolve的参数。它隐式的做了一下处理。
注意看,fetchByName('veedrin')
按道理返回的是一个Promise实例,但是我们得到的repos
值却是一个数组,这里就是await
关键字隐式处理的地方。
另外需要注意什么呢?await
关键字只能定义在async函数里面。
const then = Date.now();
function sleep(duration) {
return new Promise((resolve, reject) => {
const id = setTimeout(() => {
resolve(Date.now() - then);
clearTimeout(id);
}, duration * 1000);
});
}
async function work() {
[1, 2, 3].forEach(v => {
const rest = await sleep(3);
console.log(rest);
return '睡醒了';
});
}
work();
// Uncaught SyntaxError: await is only valid in async function
行吧,那我们把它弄到一个作用域里去。
import sleep from './sleep';
function work() {
[1, 2, 3].forEach(async v => {
const rest = await sleep(3);
console.log(rest);
});
return '睡醒了';
}
work();
不好意思,return '睡醒了'
没等异步操作完就执行了,这应该也不是你要的效果吧。
所以这种情况,只能用for循环来代替,async和await就能长相厮守了。
import sleep from './sleep';
async function work() {
const things = [1, 2, 3];
for (let thing of things) {
const rest = await sleep(3);
console.log(rest);
}
return '睡醒了';
}
work();
返回Promise实例
有人说async是Generator的语法糖。
naive,朋友们。
async可不止一颗糖哦。它是Generator、co、Promise三者的封装。如果说Generator只是一个状态机的话,那async天生就是为异步而生的。
import sleep from './sleep';
async function work() {
const needRest = await sleep(6);
const anotherRest = await sleep(3);
console.log(needRest);
console.log(anotherRest);
return '睡醒了';
}
work().then(res => console.log('?', res), res => console.error('?', res));
因为async函数返回一个Promise实例,那它本身return的值跑哪去了呢?它成了返回的Promise实例resolve时传递的参数。也就是说return '睡醒了'
在内部会转成resolve('睡醒了')
。
我可以保证,返回的是一个真正的Promise实例,所以其他特性向Promise看齐就好了。
并发
也许你发现了,上一节的例子大概要等9秒多才能最终结束执行。可是两个sleep
之间并没有依赖关系,你跟我说说我凭什么要等9秒多?
之前跟老子说要异步流程控制是不是!现在又跟老子说要并发是不是!
我…满足你。
import sleep from './sleep';
async function work() {
const needRest = await Promise.all([sleep(6), sleep(3)]);
console.log(needRest);
return '睡醒了';
}
work().then(res => console.log('?', res), res => console.error('?', res));
import sleep from './sleep';
async function work() {
const onePromise = sleep(6);
const anotherPromise = sleep(3);
const needRest = await onePromise;
const anotherRest = await anotherPromise;
console.log(needRest);
console.log(anotherRest);
return '睡醒了';
}
work().then(res => console.log('?', res), res => console.error('?', res));
办法也是有的,还不止一种。手段都差不多,就是把await
往后挪,这样既能搂的住,又能实现并发。
大总结
关于异步的知识大体上可以分成两大块:异步机制与异步编程。
异步机制的精髓就是事件循环。
通过控制权反转(从事件通知主线程,到主线程去轮询事件),完美的解决了一个线程忙不过来的问题。
异步编程经历了从回调
到Promise
到async
的伟大探索。异步编程的本质就是用尽可能接近同步的语法去处理异步机制。
async
目前来看是一种比较完美的同步化异步编程的解决方案。
但其实async
是深度集成Promise
的,可以说Promise
是async
的底层依赖。不仅如此,很多API,诸如fetch
也是将Promise
作为底层依赖的。
所以说一千道一万,异步编程的底色是Promise
。
而Promise
是通过什么方式来异步编程的呢?通过then
函数,then
函数又是通过回调来解决的。
所以呀,回调才是刻在异步编程基因里的东西。你大爷还是你大爷!
回调换一种说法也叫事件。
这下你理解了为什么说JavaScript是事件驱动的
吧?
https://github.com/veedrin/ho...
❤️ 看完两件小事
如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:
把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
关注公众号 「IT平头哥联盟」,公众号后台回复「资源
」 免费领取我精心整理的前端进阶资源教程
- 更多文章
- React vs Angular vs Vue.js——2019 该选谁?
- VS Code 1.41 发布!Web 版 VS Code 增强对 macOS or iPadOS 的支持
- 即将到来的2020年之大前端发展趋势
JS中文网 - 前端进阶资源教程 www.javascriptC.com
一个致力于帮助开发者用代码改变世界为使命的平台,每天都可以在这里找到技术世界的头条内容![]()
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。