1.js的运行机制
首先js是一种即时编译型语言,也就是单线程执行代码,当我们写js代码的时候,从上到下一行行去执行,执行完一行进行下一行。
为什么是单线程不是多线程呢?如果是多线程的话,可能同一时间下有两个body样式改变颜色,那么执行哪一个?可能同时两个线程冲突了,所以这也是为什么js单线程的原因。
但是单线程会遇到问题,如果因为一行代码运行过慢或者执行时间过长,后面的代码就会一直等待,就好像我们洗衣服1个小时,我们站在那里等吗?
这里就要涉及到事件循环,我们的js代码执行的时候虽然是单线程,但是可以分为主线程和微线程以及宏线程。
<script>
function tap() {
setTimeout(() => {
console.log("3");
}, 1000)
}
tap()
new Promise((resolve, reject) => {
resolve('2')
})
.then((result) => {
console.log(result);
})
console.log("1");
</script>
这里的js代码中,我们主线程会跳过tap函数,因为他有定时器,属于异步函数,分为宏线程,去排队,然后这里是Promise实例对象,这里我们.then是在resolve返回成功状态之后调用的函数,所以是异步,这是是微线程,所以控制台输出顺序是1,2,3.
2.回调函数
什么是回调函数,当一个函数B被作为参数传入函数A中,然后函数A被调用,执行完函数A之后回头执行你传进去的函数B,这就是回调函数。
function sayHi(callback) {
console.log("你好")
callback()
}
sayHi(() => {
console.log("我是回调函数")
})
这就是回调函数callback。
3.回调地狱
知道了回调函数我们就可以去写一个嵌套的回调函数,比如我们现在去后端拿到一个1,我们写一个函数然后里面加一个定时器,间隔1秒之后把后端的1加1。然后调用这个的时候传入一个函数作为回调函数去获取这个加工后的值并且输出出来。
function addUserAge(age,callBackFn){
setTimeout(()=>{
const result = age+1
callBackFn(result)
})
}
addUserAge(1,(result1)=>{
const age1 = result1
console.log(age1)
})
这里我们调用了一次,输出的值是2,但是如果我们想要调用多次呢?
function addUserAge(age, callBackFn) {
setTimeout(() => {
const result = age + 1
callBackFn(result)
}, 1000)
}
addUserAge(1, (result1) => {
const age1 = result1
console.log('age1', age1)
addUserAge(age1, (result2) => {
const age2 = result2
console.log('age2 ', age2)
addUserAge(age2, (result3) => {
const age3 = result3
console.log('age3 ', age3)
})
})
})
这样可读性就非常差,而且仅仅是加1这个逻辑,就变成这样,形成了我们说的回调地狱。
4.Promise
什么是Promise,是ES6推出的一门新的技术,是异步编程的新解决方案。从语法上来说是一种构造函数,功能上用来封装一个异步操作并且获取成功或者失败的结果值。
function addUserAge(age, callBackFn) {
const p = new Promise((resolve, reject) => {
setTimeout(() => {
const result = age + 1
resolve(result)
}, 1000)
})
return p
}
const p = addUserAge(1)
p.then((result1) => {
console.log('result', result1)
return addUserAge(result1)
})
.then((result2) => {
console.log('result2 ', result2)
return addUserAge(result2)
}).then((result3) => {
console.log('result3 ', result3)
})
上面的回调地狱,我们通过Promise 构造函数来包裹里面的异步函数,resolve是当成功的时候执行的回调函数,是js帮我们传进去的,我们只需要知道当异步函数执行成功后,我们调用就可以返回执行成功的结果(一个promise实例对象),然后可以用.then这个方法执行箭头函数,结果就是参数,然后执行体中去操作我们拿到的数据。比如我们希望再加工一次,那么就再次调用这个函数作为返回值,那么就又是一个新的实例对象。再次用then方法操作resolve返回的结果值。大大增加了可读性,不会造成回调地狱那种情况。便于阅读和优化代码。