一.单线程理解
javascript是单线程,且和DOM渲染共用一个进程,因为JS可以修改DOM结构,DOM渲染时JS就停止,也就是说一定时间内只能执行一段代码。
就像你一个人干家务,拖地,洗衣,缝内裤。一件一件的干,30分钟拖完地才能继续干下一件事缝补内裤,洗衣服就得等缝内裤结束后才能进行。
所以哪吒就很适合做家务,三头六臂。
二.同步理解
javascript是从上往下执行,从左往右执行代码。
既然是一行一行从上往下执行,执行中某块代码卡住了,下面的代码你就别想继续执行。
例子:
console.log(100)
alert(200)
console.log(300)
分析:先打印100 程序往下执行,浏览器弹出框显示200,不点击确定按钮,程序无法往下执行,就无法打印出300,照成了阻塞代码执行。
结论:事一件一件的干,前一件干完才能干下一件,拖着一件事三四天不干,下面的事情没法干。哎呦,这行为有点眼熟。
三.异步理解
异步是为了解决单线程等待问题。异步不按照代码顺序执行,所以执行某段代码时,没有接收到结果前,可以继续执行后续操作,不需要进行等待,是非阻塞的。
列子:
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
console.log(300)
分析:程序开始执行从上往下走,先打印100,遇见setTimeout把他先放到一边,暂存起来。程序继续往下执行,打印300。这时程序瞧着下面没有要执行的代码,跑到暂存区域看看,有个setTimeout需要执行,执行前发现还有条件,需要1秒之后执行,那就等一秒,时间一到就执行里面代码。
输出结果就是 100 , 300 , 200
四.异步的应用场景:
1.定时任务(setInterval或setTimeout) 2.网络请求(ajax) 3.绑定事件 (DOM事件监听)
五.异步和同步执行顺序:
例子
setTimeout(function(){
console.log(200)
},1000)
console.log(100)
分析:代码从上往下执行,遇见setTimeout拎出来放在暂存区,也就是任务队列里,继续执行下面代码打印100,然后进入任务队列,检查有没有任务需要执行,这个任务就叫宏任务。程序发现有就执行这个任务,打印200
也就是说,同步优先级最高。同步执行的任务放到主线程按加入顺序执行,异步任务扔到任务队列,主线程执行完之后会从任务队列里按照先入先出的方式取任务。
具体顺序就是同步>微任务>宏任务。
六.Promise理解
异步是基于callback hell 回调函数执行,但callback 回调嵌套多了,就形成回调地狱。
例子:
setTimeout(function(){
console.log(1)
setTimeout(function(){
console.log(2)
setTimeout(function(){
console.log(3)
setTimeout(function(){
console.log(4)
},1000)
},1000)
},1000)
},1000)
结构就像这样,层层嵌套,嵌套越多看着越难受,听说跟地狱一样恐怖。所以Promise出现了,目的是更加优雅地书写复杂的异步任务。
Promise只是将callback的嵌套变成了一节一节,类似于水管一样,结构清晰。但Promise本质没有变化,还是使用callback形式。
Promise 构造函数只有一个参数,还是一个函数,这个函数在构造之后会直接被异步运行,所以我们称之为起始函数。起始函数包含两个参数 resolve 和 reject,这两个参数也是函数。
new Promise(function (resolve, reject) { console.log("Run"); });
Promise 类有 .then() .catch() 和 .finally() 三个方法,这三个方法的参数都是一个函数,.then() 可以将参数中的函数添加到当前 Promise 的正常执行序列,.catch() 则是设定 Promise 的异常处理序列,.finally() 是在 Promise 执行的最后一定会执行的序列。 .then() 传入的函数会按顺序依次执行,有任何异常都会直接跳到 catch 序列:
例子:
new Promise(resolve=>{
console.log(1)
}).then(value=>console.log(2))
console.log(3)
输出结果:1 3
这里需要说明的就是new Promise 也是同步,所以会先输出1,同步继续往下走,打印3。
那么2为啥不打印?
new Promise 走到 then不会执行,会等通知,成功或者失败的状态通知。所以需要加入resolve()或reject()
new Promise(resolve=>{
console.log(1)
resolve()
}).then(value=>console.log(2))
console.log(3)
输出结果:1 3 2
分析:代码执行new Promise 打印1,then等到通知resolve(),才会把任务加入到微任务队列,继续往下走打印3,然后看看微任务队列有没有任务,发现有打印2。
上面说过同步优先级最高,然后就是微任务,最后宏任务。同步>微任务>宏任务
微任务包括:promise.then(),process.nextTick()等
宏任务包括:setTimeOut(),setInterval()等
列子:
console.log(1)
setTimeout(function(){
console.log(2)
})
new Promise(resolve=>{
console.log(3)
resolve()
}).then(value=>console.log(4))
console.log(5)
输出结果:1 3 5 4 2
分析:同步优先打印1,往下执行setTimeout,加入宏任务队列,等待处理。继续往下走new Promise时同步,所以打印3,then得到resolve()成功通知,把任务加入到微任务队列,等待处理。继续往下走打印5。同步走完,进入到微任务列队,发现任务打印出4。微任务列队没有其他任务,去宏任务列队瞧瞧,发现有任务打印出2。
例子:
console.log(1)
setTimeout(function(){
console.log(2)
})
new Promise(resolve=>{
console.log(3)
setTimeout(function(){
resolve()
console.log(4)
})
}).then(value=>console.log(5))
console.log(6)
输出结果:1 3 6 2 4 5
分析:同步优先打印1,往下执行setTimeout,加入宏任务队列,继续往下走new Promise打印3,继续执行setTimeout加入宏任务队列,then没有得到通知不执行,继续往下打印6
同步走完打印1 3 6
程序开始走微任务列队没有,走宏任务列队,任务队列里按照先入先出的方式,所以先打印2,在打印4。继续走给then发通知说成功了,然后微任务执行打印5
这就既然是微任务>宏任务,5 应该可以比4 先打印,这是因为setTimeout这条宏任务已经进入主程序,他执行完才能进入微任务,所以4 在 5 前面打印
例子:
console.log(1)
setTimeout(function(){
console.log(2)
},1000)
new Promise(resolve=>{
console.log(3)
setTimeout(function(){
console.log(4)
resolve()
})
}).then(value=>console.log(5))
console.log(6)
输出结果:1 3 6 4 5 2
分析:同步优先1 3 6 先依次打印出来,同样这里先走宏任务,先打印4 再 打印5。前面说了任务队列里按照先入先出的方式,第一个setTimeout先进入的,应该先打印2,怎么会是4和5
进入宏任务列队中,发现有两个宏任务,setTimeout1和setTimeout2。这里先说下异步执行条件,1.同步执行结束;2.异步执行状态可以执行;3.前面有可以执行的但是他条件不满足,所以你可以执行了。
按照先入先出,找的第一个setTimeout(setTimeout1),他说等他1秒。程序发现时间不满足,就去找第二个setTimeout(setTimeout2),发现他没有这多B事,就立即执行setTimeout2,打印4,继续走看还有个微任务就打印出来5。第一个setTimeout时间满足,到了1秒,开始打印2,要是设置24小时,那就等一天再打印。所以得出结论就是爱情也是一样的,不要等待
注: * 加入时间条件的setTimeout,有时候不一定在时间条件内输出,可能会超过当前时间条件,原因就是前面的任务时间太长,加上setTimeout延迟时间,输出结果时间是大于延迟时间的。*
本文章是学习知识点的梳理笔记,如有错误地方请指正,谢谢。