单线程
只有一个线程,同一时间只能做一件事
原因:避免DOM渲染的冲突
浏览器需要渲染DOM
JS可以修改DOM结果
JS执行的时候,浏览器DOM渲染会暂停
两段JS也不能同时执行(修改DOM就冲突)
webworker支持多线程,但是不能访问DOM,本质JS还是单线程
解决方案:异步
case1
{ var i, sum = 0 for(i = 0; i < 100000000; i++) { sum += i } console.log(sum) }
case2
{ console.log(1) alert('a') console.log(2) }
分析:JS执行阻塞DOM渲染,出现卡顿。因为js是单线程
case3
{ console.log(100) setTimeout(function(){ console.log(200) },100) console.log(300) }
分析:JS是单线程的,第一行打印100,第二行setTimeout是异步,暂时不执行,先往下执行,第三行打印300,代码结束。然后发现setTimout没执行,那么打印200。
event-loop
事件轮询,JS实现异步的具体解决方案
同步代码,直接执行
异步函数先放在异步队列中
待同步函数执行完毕,轮询执行异步队列的函数
case1
{ console.log(100) setTimeout(function(){ console.log(200) },100) console.log(300) }
分析:同步代码,直接执行,第一行和第三行是同步代码,直接打印100和300,第二行是异步代码,先放入异步队列。同步代码执行完毕,这时候查看异步队列,执行setTimeout打印200
case2
{ console.log(100) setTimeout(function(){ console.log(200) },100) setTimeout(function(){ console.log(300) }) console.log(400) }
分析:同步代码直接执行,第一行和第四行直接执行,打印100和400。
同步代码执行完毕,查看异步队列。这时候有2个setTimeout函数,第三行会直接放入异步队列中,那么打印300。
而第二行100ms之后才被放入异步队列中,打印200
case3
{ $.ajax({ url:'', success:function(result){ console.log('a') } }) setTimeout(function(){ console.log('b') },100) setTimeout(function(){ console.log('c') }) console.log('d') }
结果: d c a b或者 d c b a
jQuery的Deferred
dtd.resolve/dtd.reject
dtd.then/dtd.done/dtd/fail
case1
function waitHandle() { var dtd = $.Deferred() !function(dtd){ var task = function() { console.log('执行完毕') dtd.resolve() } setTimeout(task, 2000) }(dtd) return dtd } waitHandle() .then(function(){ console.log('success') })
case2
function waitHandle() { var dtd = $.Deferred() !function(dtd){ var task = function() { console.log('执行完毕') dtd.resolve() } setTimeout(task, 2000) }(dtd) return dtd.promise() } var w = waitHandle() $.when(w).then(function(){ console.log('success') })
注意:这2个case不同之处一个case是返回dtd还有一个返回dtd.promise()对象。
第一个case w.reject()不会报错,但是会出现代码混乱
第二个case w.reject()会报错,无法干预代码
Promise
回顾下语法
case
{ function loadImg(src){ return new Promise((resolve, reject)=>{ let img = document.createElement('img') img.onload = () => resolve(img) img.onerror = () => reject() img.src = src }) } const src = 'https://....jpeg' var res = loadImg(src) res.then(img => { console.log(img) }, () => { console.log('fail') }) }
异常捕获
使用catch统一捕获异常
case
{ function loadImg(src){ return new Promise((resolve, reject)=>{ let img = document.createElement('img') img.onload = () => resolve(img) img.onerror = () => reject('异常') img.src = src }) } const src = 'https://....jpeg' var res = loadImg(src) res.then(img => { console.log(img) }).catch(e => { console.log(e) }) }
多个串联
链式操作部分代码
const res = loadImg(src) const res2 = loadImg(src2) res.then(() => { console.log('one') return res2 }).then(() => { console.log('two') }).catch(e => { console.log(e) })
Promise.all和Promise.race
Promise.all接收一个promise对象的数组,待全部完成之后,统一执行then
Promise.race接收一个promise对象的数组,只要一个完成,就执行then
async/await
直接只用同步写法
case
{ function loadImg(src){ return new Promise((resolve, reject)=>{ let img = document.createElement('img') img.onload = () => resolve(img) img.onerror = () => reject('异常') img.src = src }) } const src = 'https://....jpeg' const src2 = 'https://...jpg' async function load() { const res = await loadImg(src) console.log(res) const res2 = await loadImg(src2) console.log(res2) } load() }
虽然是同步写法,但是使用了Promise,并没有改变JS时单线程,异步的本质