一、 说输出
1、变量提升问题
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000) // 1秒后一次性输出 5 5 5 5 5
}
2、优化每隔1秒输出0 1 2 3 4
/**
* 正确
*/
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i); // 0 1 2 3 4
}, 1000)
}
/**
* 最简单的一个循环
*/
for (var i = 0; i < 5; i++) {
console.log(i); // 0 1 2 3 4
}
/**
* 闭包
*/
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i); //每隔1秒输出0 1 2 3 4
}, i * 1000)
})(i)
}
/**
* 立即执行函数-node环境会报错,浏览器下输出0 1 2 3 4
*/
for (var i = 0; i < 5; i++) {
setTimeout((function(i) {
console.log(i);
})(i), i * 1000);
3、每隔1秒输出5 5 5 5 5
/**
* 内部没有对i保持引用
*/
for (var i = 0; i < 5; i++) {
(function () {
setTimeout(function () {
console.log(i); //每隔1秒输出5 5 5 5 5
}, i * 1000)
})(i)
}
/**
*
*/
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000 * i) // 每隔1秒输出一个5
}
4、宏微任务问题
/**
* Promise // 2 3 5 4 1
*/
setTimeout(function(){
console.log(1);
},0)
new Promise(function executor(resolve){
console.log(2);
for(var i=0;i<10000;i++){
i ==9999 && resolve();
}
console.log(3);
}).then(function(){
console.log(4);
})
console.log(5);
const pro = new Promise((resolve, reject) => {
const innerpro = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 0);
console.log(2);
resolve(3);
});
innerpro.then(res => console.log(res));
resolve(4);
console.log("pro");
})
pro.then(res => console.log(res));
console.log("end");
// 2 pro end 3 4
async必须返回一个promise对象,如果没有,它自己自动封装
5、对async的理解
async必须返回一个promise对象,如果没有,它自己自动封装
/**
*
* @returns async必定返回一个promise对象
*/
async function fn1() {
return 123
}
function fn2() {
return 123
}
console.log(fn1()) // Promise { 123 }
console.log(fn2()) // 123
6、await function 执行顺序是从右向左
/**
* 从右向向左执行
*/
async function async1() {
console.log('async1 start') // (1)
await async2()
console.log('async1 end') // (4)
}
async function async2() {
console.log('async2') //(2)
}
async1()
console.log('script start') // (3)
// async1 start
// async2
// script start
// async1 end
7、对js运行机制的理解
/**
* Promise // 2 3 5 4 1
*/
setTimeout(function(){
console.log(1);
},0)
new Promise(function executor(resolve){
console.log(2);
for(var i=0;i<10000;i++){
i ==9999 && resolve();
}
console.log(3);
}).then(function(){
console.log(4);
})
console.log(5);
// 2 3 5 4 1
8、宏任务和微任务
setTimeout(_ => console.log(4))
new Promise(resolve => {
resolve()
console.log(1)
}).then(_ => {
console.log(3)
})
console.log(2) //1 2 3 4
二、分析执行顺序
1、经典面试题
/**
* 经典面试题1
*/
async function async1() {
console.log("async1 start");// (2)
await async2(); // 有问题,先执行返回函数,完成后,执行下一句,也就是 先输出promise2 在输出async1 end 但是输出结果是先 async1 end 在输出promise2
console.log("async1 end");//(6)
}
async function async2() {
console.log("async2"); //(3)
}
// 开始执行任务
console.log("script start"); //宏任务1 (1)
setTimeout(function () {
console.log("setTimeout"); // 宏任务2 (8))
}, 0);
async1();
new Promise(function (resolve) {
console.log("promise1"); //(4)
resolve();
}).then(function () {
console.log("promise2"); //微任务1 (7)
});
console.log("script end");//(5)
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
2、分析
(1) 直接打印同步代码script start 2个函数声明没有调用,先不管
(2) 将setTimeout放入宏任务中,包裹的代码可以理解成宏任务2
(3) 调用async1,直接输出同步代码async1 start
(4) 遇到await从右向左执行,先执行async2输出 async2
(5) 阻塞async的执行,先执行async外的同步代码,遇到promise,直接执行同步代码promise1
(6) 运行到promise.then,发现这是一个微任务,加入当前宏任务的微任务队列中
(7) 继续执行同步代码script end
(8) 同步代码全部执行完成,回到async内部,await执行完成,执行await后面的代码 async1 end
(9) 全部的宏任务1执行完成,在执行微任务,输出promise2
(10) 宏任务1和宏任务1中微任务全部完成,进行下一个宏任务,输出setTimeout
3、宏任务和微任务
4、经典面试题
/**
* 经典面试题2
*/
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
return new Promise((resolve, reject) => {
resolve();
console.log('async2 promise');
})
}
//任务开始
console.log('script start');
setTimeout(function () {
console.log('setTimeout');
}, 0);
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
}).then(function () {
console.log('promise3');
});
console.log('script end');
// script start
// async1 start
// async2 start
// async2 promise
// promise1
// script end
// promise2
// promise3
// async1 end
// setTimeout
(1)执行同步代码script start
(2)遇到setTimeout放入下一个宏任务中
(3)调用async1,直接输出同步代码async1 start'
(4)遇到await,执行async2,直接输出同步函数async2 start
async2 promise
(5) 阻塞代码,执行async之外的同步代码 promise1
(6) 遇到then,是微任务,放入微任务中promise2
(7) 遇到then,是微任务,放到微任务中 promise3
(8) 执行同步代码'script end'
(9) 所有同步代码完成,回到async中,但是此时await并没有执行完成,await等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果,那何时能拿到运算结果呢?回忆平时我们用promise,调用resolve后,何时能拿到运算结果?是不是需要在then的第一个参数里,才能拿到结果。所以这里的 await Promise.resolve() 就类似于Promise.resolve(undefined).then((undefined) => { })把then的第一个回调参数 (undefined) => {} 推入微任务队列。
(10) 执行微任务,输出 promise2
(11)执行微任务,输出 promise3
(12) 执行微任务,没什么内容
(13) 这时候,then执行完成,await async2()语句结束,后面的代码不再被阻塞,所以打印 async1 end
(14) 执行宏任务2 setTimeout
5、await等待的不同处理
/**
* await 等待的是一个非promise 1 2 3 4 5 6
*/
async function async1() {
console.log(1);
await async2()
console.log(5);
}
function async2() {
console.log(2);
}
async1();
new Promise(function (resolve) {
console.log(3);
resolve()
}).then(function () {
console.log(6);
})
console.log(4);
/**
* await等待的是一个promise 1 2 3 4 5 6
*/
async function async1() {
console.log(1);
await async2()
console.log(6);
}
async function async2() {
console.log(2);
return new Promise((resolve, reject) => {
resolve();
})
}
async1();
new Promise(function (resolve) {
console.log(3);
resolve()
}).then(function () {
console.log(5);
})
console.log(4);
6、setTimeout
console.log('script start') //1. 打印 script start
setTimeout(function(){
console.log('settimeout') // 3. 打印 settimeout
})
console.log('script end') //2. 打印 script start
// 输出顺序:script start->script end->settimeout
7、Promise
console.log('script start')
let promise1 = new Promise(function (resolve) {
console.log('promise1')
resolve()
console.log('promise1 end')
}).then(function () {
console.log('promise2')
})
setTimeout(function(){
console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout
8、async/await
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start');
async1();
console.log('script end')
// 输出顺序:script start->async1 start->async2->script end->async1 end
三、灵魂3问
1、js为什么是单线程
想一个问题
我有2个线程,线程1,线程2,如果js是多线程,那么他可以同时对一个dom进行操作,线程1删除了dom,而线程2编辑dom,同时下达2个会矛盾的操作,会导致浏览器的崩溃。
2、为什么需要异步?
如果js没有异步,那只能依照顺序执行,如果上一段程序执行了很长时间,那么之后的代码就会阻塞,对于用户而言,阻塞就意味着页面的假死,会带来非常不好的用户体验。
3、单线程是怎么实现异步的?
单线程通过事件循环实现异步
js的事件循环,同步和异步
js的事件循环,宏任务和微任务
setTimeout(function(){
console.log('定时器开始啦')
});
new Promise(function(resolve){
console.log('马上执行for循环啦');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('执行then函数啦')
});
console.log('代码执行结束');
按照同步和异步的想法,应该输出:
【马上执行for循环啦 --- 代码执行结束 --- 定时器开始啦 --- 执行then函数啦】
但是实际的输出结果是:
【马上执行for循环啦 ---代码执行结束---执行then函数啦---定时器开始啦】