目录
1. 在JS中,什么是同步异步?
2. JS中常见的异步代码
1. 在JS中,什么是同步异步?
通俗解释一下,同步与异步:
异步:不等待结果就执行接下来的代码,一句话:不等结果!
同步:等待异步代码执行完毕后才执行接下来的代码,一句话:等结果!
先来看一段同步代码
console.log(1)
sleep(3) // 这里让控制台睡3s
console.log(2) // 这里的2会在3s后被打印出来
同步会等待sleep(3)执行完毕后才执行后面的代码
再来看一段异步代码
console.log(1)
setTimeOut(function(){console.log('3秒结束')},3000)
console.log(2) // 不等定时器,直接打印2
// 3s结束后才打印'3秒结束'
但是为啥JS是单线程的,却能实现异步代码呢?
其实上面的异步代码,在定setTimeOut闹钟时,JS引擎是委托了浏览器来监听这3s的等待,所以JS引擎的进程就空闲了,console.log(2)就能立刻执行。
而JS引擎在执行同步代码时,进程会一直堵塞,无法执行后面的代码。
2. JS中常见的异步代码
(1)获取图片宽度为0
var img = document.getElementsByClassName('img')[0].width // width为0
// 图片加载的时候,可能尚未加载完就已经执行了JS代码
解决办法
img.onload = function(){
// 在这里面获取宽度
}
(2)for循环点击li打印索引
let liList = document.getElementsByTagName('li') // 假设有5个li元素
for(let i = 0; i < liList.length; i ++){
liList[i].onclick = function(){
console.log(i) // 期望打印出0,1,2,3,4。实际打印出来5个5
}
}
for循环中,每次循环都给li绑定一个点击事件,console.log(i)中的i是同一个i,在for循环结束时,i === 5,同时绑定了如下点击事件
liList[0].onclick=function(){console.log(i)}
liList[1].onclick=function(){console.log(i)}
liList[2].onclick=function(){console.log(i)}
liList[3].onclick=function(){console.log(i)}
liList[4].onclick=function(){console.log(i)} // 注意打印的都是i,不是对应的0,1,2,3,4!
// 然鹅当用户点击li的时候,for循环已经执行完了,i已经变成5了
解决方法
- for循环中用 let 代替 i ,使每次迭代的 i 都不一样
- 用立即执行函数
let liList = document.getElementsByTagName('li') // 假设有5个li元素
for(let i = 0; i < liList.length; i ++){
!function(i){
// 由于函数立即执行,会传进来对应的i
liList[i].onclick = function(){
console.log(i) // 期望打印出0,1,2,3,4。实际打印出来5个5
}
}(i)
}
(3)AJAX
应该没人会发同步ajax吧......,万一请求不成功,JS引擎的进程就会一直被堵塞。
(4)Promise中 .then 的回调
为什么说Promise中 .then 的回调函数是异步的?来看一段代码:
promise.then(function(){
if (flag) {
foo();
} else {
setTimeout(function(){
foo();
})
}
});
bar();
我们来看下 bar 和 foo 的执行顺序
如果 then 回调是同步的:
flag 为真时,foo 先执行,然后执行bar。(bar会等上面promise代码的执行结果,执行完了才走到bar)
flag为假时,bar先执行,foo后执行。(bar会等上面promise代码的执行结果,但是执行到setTimeOut时,由于setTimeOut是异步的,所以bar就不等了,所以先执行bar)
如果 then 回调是异步的:
很明显,在promise发送请求时,bar 就已经请求了,代码执行顺序必定是先 bar 后 foo。
Promise为了保证代码的执行顺序永远保持一致,所以 then 回调采用了异步