前言
之前对async/await总是一知半解,导致有时候场景比较复杂的时候,自己就蒙了。所以这次积累总结了下。我觉得这次能耐心,安静的看下去是有收获的。如果对你们有帮助的话,还请点赞关注鼓励下,提前感谢了。在说async/await之前,我们首先看看generator。网上经常遇到这句话:async/await是generator的语法糖。这句话怎么理解了?我们一步步看下去就会知道,下面我们先解决与generator相关的几个名词。
迭代器,迭代器对象,生成器,可迭代的对象
- 迭代器,迭代器对象
我理解的迭代器和迭代器对象其实是一样的,因为迭代器本身就是一个对象,下面就以迭代器进行说明。
用自己的话概述下就是:
1:迭代器是一个拥有next方法的对象。
2:这个next返回一个达到迭代协议或者规则的对象,迭代协议或者规则对象其实就是包含value和done两个字段的对象。
- 生成器
- 可迭代的对象
什么是可迭代行为,按照我的理解是:
1:这个对象具有[Symbol.iterator]属性,并且值是函数。我们要注意不是字符串,用代码展示下更清楚。
const obj = {
[Symbol.iterator]: () => {}
}
// 不是下面这种
const obj = {
"Symbol.iterator": () => {}
}
2:[Symbol.iterator]这个函数执行后会返回一个迭代器。
generator几种写法
为什么要说这个了,主要是因为generator用的比较少,所以有时候在比较慌的情况下,会短路,还是因为没总结,下面全部写一遍。
- 具名函数
function* init() {}
- 匿名函数
function* () {}
- 函数表达式
const init = function* () {}
- 箭头函数
// 记住 generator 现在还不能用箭头函数写2021/03/24
const a = *() => {};
简单的generator用法
再来说一下generator简单的用法,都是为了说明async/await做铺垫,可以去阮一峰老师的es6课程里面熟悉下。
const init = function*(x) {
const y = yield (x + 1);
const z = yield (y / 3);
return z;
}
a = init(5);
a.next(); // {value: 6, done: false}
a.next(); // {value: NaN,done: false}
a.next(); // {value: NaN,done: true}
const init = function*(x) {
const y = yield (x + 1);
const z = yield (y / 3);
return z;
}
a = init(6);
a.next(6); // {value: 7, done: false}
a.next(6); // {value: 2,done: false}
a.next(6); // {value: 6,done: true}
来一个promise版本的--很重要
const test = function* () {
const a = yield new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3);
}, 2000);
});
return a;
}
const a = test();
console.log(a.next()); // {value: Promise, done: false}
console.log(a.next());
构造一个自执行next函数
上面展示promise版本的generator,主要是为了说明async/await的原理,后面会再说。现在我们要实现一个自执行next函数,因为async/await就是自执行的。我们先实现一个简单的架子。
const exec = g => {
const {value, done} = g.next();
if (!done) {
return exec(g);
}
}
const value = exec(g);
稍微强一点的版本
上面的实现是有一点不好,主要是为了今后我能知道怎么一步步变化过来的,上面的函数并没有传递每一次的value值,现在我们在改版下。
const g = generator();
const next = value => {
const {value, done} = g.next(value);
if (done) {
return value;
}else {
next(value)
}
}
next();
这一次的版本是不是好一点,记住这个函数很重要。
promise版本
下面为了理解asyc/await我们在来一个promise版本
const g = generator();
const next = value => {
const {value, done} = g.next(value);
if (!done) {
// value 是一个promise
value.then(val => {
next(val);
})
}
}
开始async/await认知
在说明async/await的之前我要抛出自己的三个认知,如果能帮助你们更好的理解,可以采纳,反之略过。
1:async 相当于返回一个promise对象
2:await 相当于一个yield,并放入generator函数里面
3:await 后面相当于包一层promise对象,并把值放入resolve。下面我用代码简单描述下
const test = async function() {
const a = await 123;
const b = 5 + a;
console.log(b);
}
// 将上面的代码换成我认知的那样
const test = function() {
// 认知1 async 相当于返回的是一个promise
return new Promise((resolve, reject) => {
const generator = function*() {
// 认知2 await 相当于一个yield,并放入generator函数里面
// 认知3 await 后面 相当于包一层promise对象,并把值放入resolve
const a = yield new Promise((resolve, reject) => {
resolve(123);
});
const b = 5 + a;
console.log(b);
}
// 自执行函数要加上哈
const g = generator();
const next = val => {
const {value, done} = g.next(val);
if (done) {
resolve(value);
} else {
value.then(val => next(val));
}
}
next()
})
}
按照我上面转化之后,其实我理解的async/await就是generator与promise的结合。当然源码肯定不是我这样的。现在我们就可以知道,为什么说async/await是genarator的语法糖了吧。有不对的地方可以指出,一起交流,下面用一个实际点复杂点的例子来验证,巩固下。
工作中的版本
先封装一个请求函数
const getList = async url => {
return await fetch.get(url);
}
fetch.get请求函数我们用promise模拟下,让其表现的更复杂
// 其实我觉得axios.post().then(), 最终应该还是promise的封装
const request = (url) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({result: [1, 2, 3], code: 1});
}, 500); // 模拟请求
});
}
const fetch = {
// url是虚的不要在意
get: (url) => {
// 为什么要用request在封一层,除了可以扩展下,还能让这个例子在复杂下
// 因为用到了then
return request(url).then(res => {
if (res.code === 1) {
return {...res, success: true};
}
});
}
}
实际业务代码
class Test extends React.Components {
async componentDidMount() {
const data = await getList(url);
console.log('我是什么时候执行');
console.log(data);
}
}
上面的例子componentDidMount是一个async/await,而getList还是一个async/await,并且fetch.get的返回是request(url).then()。那么下面的打印“我是什么时候执行”会一直等到data有值了才会执行么?
答案是肯定的
分析
分析1
我们先用async/await去分析,其实前面几个内容很好分析componentDidMount是async/await,遇到了getList继续async/await下去。但是很多人可能在fetch.get这里迷糊了。这个函数不是已经return了么,data应该没有值哇。这么想的话首先是async/await没有深入理解然后就是没有对promise进行深入理解,下面按照我上面的三层认知来进行刨析。
分析2
2-1
按照我们上面的分析,将第一个函数改造下
async componentDidMount() {
const data = await getList(url);
console.log('我是什么时候执行');
console.log(data);
}
// 改成如下
const componentDidMount = function() {
return new Promise((resolve, reject) => {
const generator = function*() {
const data = yield new Promise((resolve, reject) => {
resolve(getList(url));
});
console.log('我是什么时候执行');
console.log(data);
}
// 转化成自执行函数形式,参考上面
const g = generator();
const next = val => {
const {value, done} = g.next(val);
if (done) {
resolve(value);
} else {
value.then(val => next(val));
}
}
next()
})
}
- 我们可以看一下上面改造代码,如果想要console.log('我是什么时候执行')这句执行,是不是就要等到value.then运行,那么我们在看看value是什么?从上面可知value其实就是yield后面的promise对象。
new Promise((resolve, reject) => {
resolve(getList(url));
});
那么value.then的执行就取决于resolve(getList(url))
-
再来分析getList(url),这个函数返回什么。getList也是一个async/await。按照我上面认知1,async返回的是一个promise对象,那么就相当
resolve(new Promise())
。 -
我强烈建议看一下我之前写的一篇关于promise源码这篇文章。看完之后你就会知道,如果一个promise(称作A)resolve里面还是一个promise(称作B),那么A的执行取决于B什么时候执行resolve。
-
意思就是说console.log('我是什么时候执行'),这句什么时候执行取决于getList的resolve执行时机
2-2
我们来到getList发现其实和componentDidMount一样,我们还是改造下
const getList = async url => {
return await fetch.get(url);
}
// 改造之后
getList = function() {
return new Promise((resolve, reject) => {
const generator = function*() {
yield new Promise((resolve, reject) => {
resolve(fetch.get(url));
});
}
// 转化成自执行函数形式,参考上面
const g = generator();
const next = val => {
const {value, done} = g.next(val);
if (done) {
resolve(value);
} else {
value.then(val => next(val));
}
}
next()
})
}
-
这里要说明一点的是getList什么时候resolve,是取决于这行代码
if (done) {resolve(value)}
,想要他触发那么必须就要done是true,必然要把第一个yield执行完。 -
我们看看第一个yield,还是一个promise对象。要想value.then执行必须先执行
resolve(fetch.get(url))
。有点熟悉了吧。 -
fetch.get 返回如下:request(url).then()。问题的最关键地方来了,这是一个promise么?如果不是没什么好说的,直接返回。如果是一个promise就需要继续分析下去,因为我们的500毫秒的停顿还没开始了。
-
了解promise源码的人应该知道,then就是返回一个新promise对象。
-
resolve(fetch.get(url))返回又是一个新的promise,那么resolve的执行是不是需要看fetch.get(url)返回的这个promise对象什么执行resolve。换句话说就是看request(url).then()这个promise什么时候resolve。
2-3
再次对promise进行解释。then返回的promise什么执行,其实就是then什么时候执行。放一段我之前写的then的源码。
if (self.info.status === "pending") {
self.onFulfilledArr.push((data) => {
setTimeout(() => {
try {
let value = data;
value = onFulfilled(value);
resolve(value);
} catch (e) {
reject(e);
}
});
});
我们会看到,只要then函数(第一个回调)被执行了,那么直接获取到值,然后resolve了。回到request(url).then()这句代码,then什么时候被执行,是不是看request(url)这个promise什么时候resolve,根据代码可以得出他是在500毫秒之后resolve的。可能你现在有点没串起来我下面大概捋一下。
2-4
async/await(compontDidMount)----->需要等到getList()这个promise对象resolve才能继续next(),才会去打印----->而getList()的resolve是要等到fetch.get()或者说request(url).then() resolve了才会执行----->而request(url).then()这个promise的resolve需要等到then函数执行----->然后then函数回调执行需要等到request(url)返回的promise对象resolve----->根据代码可知request(url)返回的promise对象在500毫秒之后resolve----->也就是整个代码由于generator函数会一直等待,直到500毫秒之后resolve了才去next。
总结
看了上面的总结,你可能对async/await有一个新的认识。网上很多文章是深入源码讲解。但是我这一篇文章是将async/await转化成promise + generator进行说明。当然如果你还想知道generator是怎么实现的,可以在网上搜一下。
参考文章
Async / Await / Generator 实现原理
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!