小试牛刀,来几个简单的异步问题引出易错小知识点
一、Promise
// Question1
new Promise(resolve => {
console.log('promise')
}).then(function() {
console.log('promise1')
}).then(function() {
console.log('promise2')
})
输出什么(别说我没提醒,仔细看)?
再来两个
//Question2
new Promise(resolve => {
resolve(1)
}).then(function(res) {
console.log('promise1')
}).then(function(res) {
console.log(res)
})
输出什么呢?
//Question3
new Promise(resolve => {
resolve(1)
}).then(function(res) {
console.log('promise1')
return res
}).then(function(res) {
console.log(res)
})
//Question4
new Promise(resolve => {
resolve(1)
}).then(1).then(function(res) {
console.log(res)
})
这个输出什么呢?
这个有几个小小知识点,如果您已经知道可以略过
- promise有三个状态,只有变为成功状态才会执行.then中的内容
- pending[待定]初始状态
- fulfilled[实现]操作成功
- rejected[被否决]操作失败
- .then会接收上一任务的返回值,如果.then中的操作没有意义会被忽略
接下来我们看一下输出
//Question1
new Promise(resolve => {
console.log('promise')
}).then(function() {
console.log('promise1')
}).then(function() {
console.log('promise2')
})
//promise
//没有resolve,不执行后续操作
//Question2
new Promise(resolve => {
resolve(1)
}).then(function(res) {
console.log(res)//接受resolve传过来的1
}).then(function(res) {
console.log(res)//上一步没有返回值
})
//1 undefined
//Question3
new Promise(resolve => {
resolve(1) //状态成功,往下执行
}).then(function(res) {
console.log('promise1')
return res
}).then(function(res) {
console.log(res)//接受上一步的返回值
})
//promise1 1
//Question4
new Promise(resolve => {
resolve(1)//成功,往下执行
}).then(1).then(function(res) {
console.log(res)//.then(1)被略过,接收resolve(1)传过来的1
})
//1
其他Promise的知识点就不做赘述了,仅列出几个不容易注意的和我经常犯错的
二、async/await
再来看一个简单的小例子
async function async1() {
console.log(1)
await waitHandle()
console.log(2)
}
async1()
function waitHandle(){console.log(3)}
这个很简单,输出为1,3,2,但是原理是什么呢,实际上他是这样执行的
function async1(){
console.log(1)
return new Promise((res) => {
console.log(3)
}).then((res) => {
//await执行完毕后才会执行后面的操作
console.log(2)
})
}
三、setTimeout
//3.1
setTimeout(function () {
console.log('1');
}, 0);
setTimeout(function () {
console.log('2');
})
//3.2
setTimeout(function () {
console.log('1');
}, 10);
setTimeout(function () {
console.log('2');
})
结论:3.1 1 2
结论:3.2 2 1
与时间有关,谁先执行完谁输出
setTimeout(function () {
console.log('1');
new Promise(function (resolve) {
console.log('2');
resolve();
}).then(function () {
console.log('3')
})
}, 0);
setTimeout(function () {
console.log('6');
new Promise(function (resolve) {
console.log('7');
resolve();
}).then(function () {
console.log('8');
});
})
- 两个宏任务,时间没有差别,按顺序执行
- 先输出1
- 遇到微任务,要执行完宏任务中所有微任务才能进入下一块宏任务
输出 2 3 - 所有微任务执行完 进入下一个宏任务,一样的步骤 **输出6 7 8 **
上面的再稍稍修改下
setTimeout(function () {
console.log('1');
new Promise(function (resolve) {
console.log('2');
resolve();
}).then(function () {
console.log('3')
//新增一个setTimeout
setTimeout(function () {
console.log('9');
new Promise(function (resolve) {
console.log('10');
resolve();
}).then(function () {
console.log('11');
});
})
})
}, 0);
setTimeout(function () {
console.log('6');
new Promise(function (resolve) {
console.log('7');
resolve();
}).then(function () {
console.log('8');
});
})
区别在于第一个宏任务中又包含一个宏任务,那应该是怎么处理,执行所有的微任务,宏任务当然放到下一轮宏任务去处理啦
图片占位2
所以相比上一个新增的宏任务要接着上一步的解析接着写
- 两个宏任务,时间没有差别,按顺序...
- ...务,一样的步骤 **输出6 7 8
- 输出9 10 11
四、实战开始
前面给像我一样基础不扎实的小伙伴做铺垫用,接下来进入正文,面试中经常考到的执行顺序不懂的时候令人头疼,仔细分析下来会爱上分析的过程
想弄明白执行顺序有几个不得不知的概念(都是重点)
- 同步任务:众所周知js是单线程,一个个任务排排站一个一个的执行,称之为同步任务,放入主线程队列
- 异步任务:区别于主线程任务,放在等待队列中执行,等所有主线程任务全部结束后再执行等待队列中的任务,但谁先执行完谁输出(例如settimeout设置时间为0的比设置为100的先输出,尽管为0的是后天加进等待队列的),其余的视为“按顺序”执行
- 微任务、宏任务:执行等待队列也是有“规则“而言的,先执行微任务,后执行宏任务,两个宏任务要执行完里面的任务后再执行下一个宏任务,如果宏任务里面还有宏任务要把宏任务抛出来放到下一轮执行
常见的微任务:
- Promise(async/await):Promise并不是完全的同步任务,可以理解为状态完成后.then中的操作要放入等待队列中,await同理
常见的宏任务:
- 定时器
- 事件绑定
- ajax
- 回调函数
- Node中fs可以进行异步的I/O操作
结束枯燥的概念时间(虽然枯燥但是一定要看,很有用处,下文会尽量减少文字),我们来小小的检验一下理论的知识带来的成果
//网络经典题1,先做题再对答案再看分析哦~
Promise.resolve().then(() => {
console.log('promise1');
const timer2 = setTimeout(() => {
console.log('timer2')
}, 0)
});
const timer1 = setTimeout(() => {
console.log('timer1')
Promise.resolve().then(() => {
console.log('promise2')
})
}, 0)
console.log('start’);
思路:首先先分队列,分出同步队列任务和等待队列任务
- 第一步中,Promise直接返回成功状态,.then中的操作为异步块,将其中的操作放入等待队列
- setTimeou为异步任务,将里面操作也放入等待队列
主线程任务 | 等待队列 |
---|---|
console.log('start’) | console.log('promise1'); |
-- | const timer2 = setTimeout(() => {console.log('timer2')}, 0) |
-- | console.log('timer1') |
-- | Promise.resolve().then(() => {console.log('promise2')}) |
3.接下来先清空主线程任务。输出start
4.等待队列中,按规则输出(先同步,后微任务,最后宏任务)
5.输出promise1 和 timer1
6.微任务优于宏任务 输出promise2
7.最后宏任务 输出timer2
最后结果:start promise1 timer1 promise2 timer2
按照上面的顺序 我们再来试一个
//网络经典题2,先做题再对答案再看分析哦~
async function async1() {
console.log(1)
await async2()
console.log(2)
}
async function async2() {
setTimeout(function () {
console.log(3)
},99.999)
}
console.log(4)
setTimeout(function () {
console.log(5)
},100)
async1()
new Promise(function (resolve) {
console.log(6)
resolve()
}).then(function () {
console.log(7)
})
console.log(8);
- 从上往下看,定义了两个async函数,暂时未调用,不做操作不放入队列
- 遇到console.log(4),输出 4
- 遇到setTimeout放入等待队列
- 调用async1,执行async1内容,遇到console.log(1),输出1
- 遇到async2,执行async2内容,async2内容是setTimeout放入等待队列
- await后的内容相当于放入.then中执行所以console.log(2)放入等待队列,这步分析可以参考二、async
- 继续执行,遇到new Promise先执行里面内容 console.log(6)输出6
- 将Promise .then中的操作console.log(7)放入等待队列
- 主线程任务最后一个同步任务 console.log(8) 输出8
- 主线程任务执行完毕看等待队列中内容,按规则执行,先把同步的任务先输出出来console.log(2) console.log(7) 输出 2 7
- 再比较省下两个setTimeout,显而易见先输出3后输出5
最后输出 4 1 6 8 2 7 3 5
是不是有点理解了?就是把任务一步一步分出去,最后按顺序按规则输出,其中的坑已经在前面都总结了一些,接下来自己试一试吧
console.log('1');
setTimeout(function () {
console.log('2');
new Promise(function (resolve) {
console.log('3');
resolve();
}).then(function () {
console.log('4');
})
}, 0);
new Promise(function (resolve) {
console.log('5');
resolve();
}).then(function () {
console.log('6');
});
setTimeout(function () {
console.log('7');
new Promise(function (resolve) {
console.log('8');
resolve();
}).then(function () {
console.log('9');
});
})
图片占位
这个问题眼熟么,就是第三部分中的原题啊!!看过透题的小伙伴应该可以答对了吧,答不对的抽自己两巴掌回去再看一下~
最后结果:1 5 6 2 3 4 7 8 9
五、自己试试吧
看完前面如果如果我写的足够通俗并且您也仔细看过了,正确率应该能达到90%,剩下10%可能是有些马虎或者漏掉了需要注意的地方,也可能是我道行浅遇到的问题不够多
//5.1
async function asy2() {
return new Promise(resolve => {
setTimeout(function () {
console.log(1)
});
resolve(2)
}).then(value => {
console.log(value)
})
}
async function asy() {
await asy2();
console.log(3)
}
asy()
//5.2
async function async1() {
console.log(1)
await async2()
console.log(2)
return await 3
}
async function async2() {
console.log(4)
}
setTimeout(function() {
console.log(5)
}, 0)
async1().then(v => console.log(v))
new Promise(function(resolve) {
console.log(6)
resolve();
console.log(7)
}).then(function() {
console.log(8)
})
console.log(9)
//5.3
(function() {
setTimeout(() => {
console.log(0);
});
new Promise(resolve => {
console.log(1);
setTimeout(() => {
resolve();
Promise.resolve().then(() => {
console.log(2);
setTimeout(() => console.log(3));
Promise.resolve().then(() => console.log(4));
});
});
Promise.resolve().then(() => console.log(5));
}).then(() => {
console.log(6);
Promise.resolve().then(() => console.log(7));
setTimeout(() => console.log(8));
});
console.log(9);
})();
搜集了一些常见的题,大家试一下吧
5.1 =>2 3 1
5.2 => 1 4 6 7 9 2 8 3 5
5.3 => 1 9 5 0 6 2 7 4 8 3
六、结束
本文用于总结从蒙顺序到自己开始分析的学习过程,用词也并不专业,如有错误及补充欢迎大家指出,共同进步,希望本篇文章能帮助到和我曾经一样有疑惑的同学