这里写目录标题
回调地狱
回调地狱: 因为js是单线程的, 有些时候为了代码功能需求,需要函数嵌套函数,当函数嵌套多层时,就会形成回调地狱
如何解决回调地狱: 通过Promise() 解决
模拟异步操作
<script>
// 模拟异步操作 (下面例子当调用函数fun2()执行顺序 2 1 3)
function fun1(){
setTimeout(function(){
console.log('setTimeout1');
fun3()
}, 1000)
}
function fun2(){
setTimeout(function(){
console.log('setTimeout2');
fun1()
}, 2000)
}
function fun3(){
setTimeout(function(){
console.log('setTimeout3');
}, 2000)
}
fun2();//输出顺序 2 1 3
</script>
回调地狱实例
function fun() {
setTimeout(function () {
console.log(2);
setTimeout(function () {
console.log(1);
setTimeout(function () {
console.log(3);
})
})
})
}
fun(); 执行顺序是 2 1 3
Promise() 对象
Promise是什么
:Promise是异步编程的一种解决方案,从语法上讲是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
Promise() 对象:
存在三种状态:
-
进行时 pending
-
成功 resolve
-
失败 reject
状态的改变 只能从 pending 转换为 resolve; 或者 从pending 转换为 reject; 如果处于pending状态,永远不知道下一步转换为什么状态
Promise() 接受一个函数作为参数; 函数存在两个参数(这两个参数是js原生提供的) 一个是resolve, 一个是reject
Promise()的执行机制:
如果Promise()执行成功,则会调用执行 resolve(); resolve()中的参数就是执行成功的结果,通过then() 进行接受, then() 参数是一个函数,函数的参数就是 resolve传递传递出来的数据
如果Promise()执行失败, 则会调用执行 reject(); reject()中的参数就是执行失败的错误信息, 通过 catch()进行接受, catch的参数一个函数。 函数的参数 err 就是reject 传递出来的错误信息
上实例
<script>
var p1 = new Promise(function(resolve, reject){
if(Math.random() > 0.5){//二分之一的概率成功或失败
// 执行成功
// resolve() 会自动调用 then() 中的回调函数, resolve()的参数(数据)就是 then() 中回调函数的接受的数据
resolve(' 成功的结果');
}else{
// 执行失败
// reject() 会自动调用 catch()中的回调函数, reject()的参数(错误信息) 就是catch()中的回调函数接受的数据
reject(' 失败的信息')
}
})
p1.then(function(data){
console.log('promise 成功执行了' + data); // promise 成功执行了 成功的结果
}).catch(function(err){
console.log('promise 执行失败' + err); // promise 执行失败 失败的信息
})
</script>
官方解释
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise()基本结构:
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数; 因为这样, 可以通过Promise() 解决回调地狱的问题
Promise的缺点
● 无法取消Promise,一旦新建它,就会立即执行,无法中途取消
● 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
● 当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
Promise.all()方法
10个ajax请求 全部执行结束 然后在去做下一件事情 ; 如何判断10个请求都执行完成结束了呢?
Promise.all() 方法可以解决这个问题:
接受的参数是一个数组, 数组中的每个值是promise 实例对象, all()的状态是由参数的状态的决定 ; 所有的promise对象都成功则成功,有一个失败则失败
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。(返回的结果是一个新的Promise()对象)
<script>
function fun1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('setTimeout1');
resolve('setTimeout1 的 data');
}, 1000)
})
}
function fun2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('setTimeout2');
resolve('setTimeout2 的 data2');
}, 1500)
})
}
function fun3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('setTimeout3');
resolve('setTimeout3 的 data3');
}, 2000)
})
}
// Promise.all() 参数是一个数组, 数组中的每个值都是Ppromise()的实例对象,这里也算是调用
const p = Promise.all([fun1(), fun2(), fun3()]);
// p 的状态 有 fun1(), fun2(), fun3() 决定,只有fun1(), fun2(), fun3() 都是成功,则 p的 状态是 成功 fulfilled ; 如果 fun1(), fun2(), fun3() 存在一个失败,则 p的状态 是失败rejected
console.log(p);
p.then((data) => {
// then()方法的回调函数的参数 是 所有 Promise()实例执行成功时传递的数据,自动把所有的数据放到一个数组中
console.log(data);// (3) ["setTimeout1 的 data", "setTimeout2 的 data2", "setTimeout3 的 data3"]
console.log('三个函数都执行结束');
}).catch((err) => {
// catch() 方法 优先捕获失败的信息, 谁先失败 就捕获谁
console.log(err);
});
</script>
看输出代码
我们让其一个值状态为失败,看下是不是这样呢
function fun2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('setTimeout2');
reject('setTimeout2 的 data2');
}, 1500)
})
}
Promise.race() 方法
Promise.race() 方法 的参数是一个数组, 数组中的每个值是 Promise() 实例对象; 最后返回一个新的Promise() 对象
状态 改变: fun1(), fun2(), fun3() 三个实例对象 谁的状态先改变(不管成功还是失败),则p的状态都会随之跟随改变 如果fun1(), fun2(), fun3() 三个实例对象 先改变的状态是成功,则成功,如果失败就是失败
<script>
function fun1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('setTimeout1');
// resolve('setTimeout1 的 data1 成功');
reject('setTimeout1 第一个失败');//因为fun1()的状态先改变失败,所以为失败
}, 1000)
})
}
function fun2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('setTimeout2');
resolve('setTimeout2 的 data2 成功');
// reject('setTimeout2 的 失败');
}, 1500)
})
}
function fun3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('setTimeout3');
// resolve('setTimeout3 的 data3 成功');
reject('setTimeout3 失败了');
}, 2000)
})
}
// Promise.race() 方法 的参数是一个数组, 数组中的每个值是 Promise() 实例对象; 最后返回一个新的Promise() 对象
// 状态 改变: fun1(), fun2(), fun3() 三个实例对象 谁的状态先改变(不管成功还是失败),则p的状态都会随之跟随改变 如果fun1(), fun2(), fun3() 三个实例对象 先改变的状态是成功,则成功,如果失败则失败
//
const p = Promise.race([fun1(), fun2(), fun3()]);
console.log(p);
p.then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
</script>
我们看下先改变的值为成功的情况
function fun1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('setTimeout1');
resolve('setTimeout1 的 data1 成功');
}, 1000)
})
}
async 函数
await 是一个修饰符,只能放在async定义的函数内。可以理解为等待。
快速使用例子:
function A() {
console.log("A执行了");
};
function B() {
console.log("B执行了");
};
async function C() {
await A()
await B()
}
C()
function A() {
console.log("A执行了");
};
function B() {
console.log("B执行了");
};
async function C() {
await B()
await A()
}
C()
加个定时器看执行情况
function A() {
setTimeout(() => {
console.log("A执行了");
}, 1000)
};
function B() {
console.log("B执行了");
};
async function C() {
await A()
await B()
}
C()
1秒钟后A执行
async 函数 是一个异步操作, 返回的是一个Promise()对象
async function f() {
return 'hello world';
}
f().then(v => console.log(v))//因为返回是Promise()对象,所以有then方法
await 等待 ; 在 async函数中使用 通常情况下 await 后是一个Promise()对象, 如果不是,会自动转换为Promise()对象
<script>
//async 函数 是一个异步操作, 返回的是一个Promise()对象
// await 等待 ; 在 async函数中使用 通常情况下 await 后是一个Promise()对象, 如果不是,会自动转换为Promise()对象
async function fun(val) {
// console.log(1);
let res=await fun2();// await 等待的结果 最终就是一个具体的值,
return 'hello ' + res;
}
async function fun2() {
// console.log(2);
return 'world';
}
fun().then(function(res){
console.log(res);//hello world
})
</script>
<script>
async function fun() {
let res3 = await new Promise((resolve) => {
resolve('这是Promise对象');
})
return 'hello ' + res3;//async和await基础.html:39 hello 这是Promise对象
}
fun().then(function (res) {
console.log(res);
})
</script>
当Promise()方法 有多个then()时用 async方法
<script>
// 基础函数
function longTime(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(time + 500)
}, time)
})
}
// 三个函数 time1() ==> time2() ===> time3() 通过Promise() 实现
// 1的输出值2要用,2的输出值3要用,然后输出3的值
function time1(n){
return longTime(n);//这是一个方法
}
/* time1(500).then(res1 => {
console.log(res1);//1000
time1(res1).then(res2 => {
console.log(res2);//1500
time1(res2).then(res3 => {
console.log(res3);// 2000
})
})
})
/* 也可以直接用基础函数
longTime(500).then(res1 => {
return longTime(res1);
}).then(res2 => {
console.log(res2); 1500
return longTime(res2) ;
}).then(res3 => {
console.log(res3); //2000
})*/
/* ====上述方法的简写
time1(500).then(res1 => {
return time1(res1);
}).then(res2 => {
// console.log(res2); 1500
return time1(res2) ;
}).then(res3 => {
console.log(res3); //2000
})
*/
// 使用 async 函数实现
/*
async function test(n){
let res = await longTime(n)
// console.log(res);
return res;
}
test(500).then(res1 => {
// console.log(res1);
return longTime(res1)
}).then(res2 => {
// console.log(res2);
return longTime(res2)
}).then(res3 =>{
console.log(res3); //2000
})*/
// 如果存在三个函数这样写
async function time1(n){
return await longTime(n)
}
async function time2(n){
return await longTime(n)
}
async function time3(n){
return await longTime(n)
}
async function test(n){
let res1 = await time1(n);
let res2 = await time2(res1);
let res3 = await time3(res2);
console.log(res3); //2000
}
test(500);
</script>
宏任务和微任务
来源
js 是单线程执行的,js中的任务按顺序一个一个的执行,但是一个任务耗时太长;
那么后面的任务就需要等待,为了解决这种情况,
将任务分为了同步任务和异步任务;
而异步任务又可以分为微任务和宏任务。
宏任务和微任务的分类
宏任务:
setTimeout(定时器每隔多久后执行一次这个函数)
setInterval(定时器只在指定时间后执行一次这个函数)
微任务:
process.nextTick(node.js中的内容)
Promise的then方法
new Promise((r) => {
console.log("1");//构造函数是同步的(不属于宏任务也不属于微任务)
r();
}).then(() => {
console.log("3");//then()是异步的,这里已经入队
});
console.log("2");
//1
//2
//3
在没有async(等待)执行过程是
-
执行所有同步的,然后执行异步的
-
存在微任务的话,那么就执行所有的微任务
-
微任务都执行完之后,执行下一个宏任务
小知识:定时器(宏任务)是最后执行
具体解释在
微任务和宏任务的执行顺序
事件循环中的宏任务和微任务执行顺序
示例:
setTimeout(() => {
//执行后 回调一个事件
console.log('事件1')//异步宏任务
}, 0)
console.log('事件2');//同步任务
this.$nextTick(function() {
console.log('事件3');//异步微任务
})
new Promise((resolve) => {
console.log('事件4');//同步任务
resolve()
}).then(() => {
console.log('事件5');//异步微任务
})
执行顺序是:
解释:
事件1 setTimeout异步事件,放入异步任务队列;
事件2 所在行是主线程同步,直接执行;
事件3所在行也异步事件,放入异步任务队列;
创建promise的new Promise是主线程,直接执行;
Promise中的then方法为异步函数,放入异步任务队列;
先执行同步,在执行异步(先执行微任务,在执行宏任务)
描述this.nextTick()的用法DOM更新循环之后执行
在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:
Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '未更新'
}
},
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
}
}
})
因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的 ES2017 async/await
语法完成相同的事情:
methods: {
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // = '未更新'
await this.$nextTick()
console.log(this.$el.textContent) // = '已更新'
}
}
Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM
【Vue面试题】说说nextTick的使用和原理?