js是一個(gè)單線程的腳本語(yǔ)言,之所以是單線程是由于js要操作dom掘剪,如果在同一時(shí)間,一個(gè)線程修改dom奈虾,一個(gè)線程刪除dom夺谁,這個(gè)時(shí)候?yàn)g覽器會(huì)不知該如何處理,為了避免這種情況的出現(xiàn)肉微,所以js只能按照順序從上到下執(zhí)行匾鸥,這種從上到下按順序執(zhí)行就是同步任務(wù)。
一碉纳、同步任務(wù)和異步任務(wù)
js里的“同步”指的是代碼從上到下按順序執(zhí)行勿负,這種執(zhí)行方式有一個(gè)問(wèn)題,當(dāng)遇到計(jì)算量比較大的任務(wù)時(shí)村象,代碼會(huì)等待計(jì)算結(jié)束再繼續(xù)執(zhí)行笆环,這個(gè)時(shí)候界面會(huì)卡死不動(dòng),為了解決這個(gè)問(wèn)題厚者,就有了“異步”這個(gè)概念躁劣;“異步任務(wù)”指的就是當(dāng)遇到計(jì)算量比較大的函數(shù)時(shí),給這個(gè)函數(shù)單獨(dú)開(kāi)一個(gè)線程去運(yùn)行這個(gè)計(jì)算費(fèi)時(shí)的任務(wù)库菲,代碼繼續(xù)執(zhí)行账忘,當(dāng)計(jì)算完成后,再執(zhí)行計(jì)算后回調(diào)函數(shù)內(nèi)的代碼。
js引擎通過(guò)事件循環(huán)的機(jī)制來(lái)處理什么時(shí)候執(zhí)行同步代碼鳖擒,什么時(shí)候執(zhí)行異步代碼
二溉浙、事件循環(huán)、宏任務(wù)蒋荚、微任務(wù)
js在執(zhí)行的時(shí)候戳稽,可以理解為是一個(gè)函數(shù)調(diào)用棧,當(dāng)執(zhí)行到某個(gè)函數(shù)時(shí)期升,將當(dāng)前這個(gè)函數(shù)壓入調(diào)用棧惊奇,當(dāng)執(zhí)行結(jié)束后,彈出調(diào)用棧播赁;如果遇到異步任務(wù)時(shí)颂郎,調(diào)用棧不會(huì)等待函數(shù)執(zhí)行完畢,而是開(kāi)啟這個(gè)異步任務(wù)后容为,將該函數(shù)彈出調(diào)用棧乓序;當(dāng)異步任務(wù)執(zhí)行成功后,會(huì)放入一個(gè)單獨(dú)的事件隊(duì)列里坎背,當(dāng)同步函數(shù)都執(zhí)行完成后替劈,事件循環(huán)機(jī)制會(huì)在這個(gè)事件隊(duì)列里查找是否有需要執(zhí)行的函數(shù),如果有得滤,就會(huì)依次壓入函數(shù)調(diào)用棧抬纸;
function fn1 () {
console.log('fn1')
}
function fn2 () {
setTimeout(()=>{
console.log('fn2')
}, 2000)
}
console.log('fn----')
fn2()
fn1()
// fn----
// fn1
// fn2
上面代碼中,調(diào)用棧依次是:
1耿戚、console.log('fn----'); 入棧 出棧
2、fn2入棧阿趁,setTimeout入棧膜蛔,監(jiān)測(cè)到是一個(gè)異步任務(wù),單獨(dú)開(kāi)辟一個(gè)線程計(jì)時(shí)脖阵,2s后放入事件隊(duì)列里皂股;setTimeout出棧,fn2出棧
3命黔、fn1入棧呜呐, console.log('fn1')入棧, console.log('fn1')出棧悍募,fn1出棧
4蘑辑、所有同步代碼執(zhí)行完成后,事件循環(huán)檢測(cè)事件隊(duì)列里是否有需要執(zhí)行的函數(shù)坠宴,當(dāng)2s后發(fā)現(xiàn)了console.log('fn2')需要執(zhí)行洋魂,將其入棧,出棧
5、至此副砍,所有代碼執(zhí)行完成
如果有很多異步任務(wù)衔肢,這些異步任務(wù)都會(huì)在倒計(jì)時(shí)結(jié)束后進(jìn)入到事件隊(duì)列里排隊(duì),這些在事件隊(duì)列里排隊(duì)的任務(wù)就是宏任務(wù)豁翎,還有一種異步任務(wù)不會(huì)在這個(gè)事件隊(duì)列里排隊(duì)角骤,只要是當(dāng)前任務(wù)執(zhí)行結(jié)束,而新的任務(wù)還沒(méi)開(kāi)始心剥,這個(gè)時(shí)候會(huì)優(yōu)先執(zhí)行這個(gè)異步任務(wù)邦尊,這個(gè)就是微任務(wù),promise就是其中之一
function fn1 () {
console.log('fn1')
}
setTimeout(()=>{
console.log('setTimeout---')
},0)
Promise.resolve().then(()=>{
console.log('promise---')
})
fn1()
// fn1
// promise---
// setTimeout---
上面代碼中刘陶,Promise的執(zhí)行要先于setTimeout
三胳赌、回調(diào)函數(shù)
在處理異步任務(wù)時(shí),傳入一個(gè)回調(diào)函數(shù)是之前比較常用的寫(xiě)法
function fn (callBack) {
setTimeout(()=>{
callBack('done')
}, 200)
}
fn((data)=>{
console.log(data)
})
這種寫(xiě)法的缺點(diǎn)是處理復(fù)雜的異步任務(wù)時(shí)匙隔,容易產(chǎn)生回調(diào)嵌套的問(wèn)題疑苫,嵌套過(guò)深不利于閱讀,也不利于維護(hù)纷责,ES6中新增的Promise可以解決這個(gè)問(wèn)題捍掺。
四、Promise
關(guān)于Promise的學(xué)習(xí)筆記已經(jīng)整理在另一篇文章中再膳,這里不做重復(fù)
理解promise
五挺勿、Generator
使用generator實(shí)現(xiàn)"理解promise"文章中的例子
let data = {
story: ['title1', 'title2', 'title3', 'title4', 'title5', 'title6'],
title1: '第一段段落,第一段段落第一段段落第一段段落第一段段落第一段段落第一段段落第一段段落第一段段落',
title2: '第二段段落喂柒,第二段段落第二段段落第二段段落第二段段落第二段段落第二段段落第二段段落第二段段落',
title3: '第三段段落不瓶,第三段段落第三段段落第三段段落第三段段落第三段段落第三段段落第三段段落第三段段落',
title4: '第四段段落,第四段段落第四段段落第四段段落第四段段落第四段段落第四段段落第四段段落第四段段落',
title5: '第五段段落灾杰,第五段段落第五段段落第五段段落第五段段落第五段段落第五段段落第五段段落第五段段落',
title6: '第六段段落蚊丐,第六段段落第六段段落第六段段落第六段段落第六段段落第六段段落第六段段落第六段段落'
}
let time = {
story: 1000,
title1: 3000,
title2: 2000,
title3: 1000,
title4: 4000,
title5: 5000,
title6: 6000,
}
var getData = function (key) {
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(data[key])
}, time[key])
})
}
// 定義生成器函數(shù)
function * main () {
const titleLists = yield getData('story')
for(let item of titleLists) {
yield getData(item)
}
}
const g = main()
function handle (result) {
if (result.done) {
return;
}
result.value.then((tittle)=>{
let _result = g.next(tittle)
// 遞歸調(diào)用
handle(_result)
})
}
// 函數(shù)開(kāi)始執(zhí)行
handle(g.next())
上面代碼中都是按順序執(zhí)行,即獲取完第一段落再請(qǐng)求第二段落艳吠,優(yōu)化后的代碼如下:
function * main () {
const titleListsPromise = yield getData('story')
for(let itemPromise of titleListsPromise) {
yield itemPromise
}
}
function handle (result) {
if (result.done) {
return
}
result.value.then((content)=>{
console.log(content)
result = g.next()
handle(result)
})
}
const g = main()
let re = g.next()
re.value.then((titleLists)=>{
let _promiseLists = titleLists.map(getData)
handle(g.next(_promiseLists))
})
六麦备、Async/await
Async/await其實(shí)是generator的語(yǔ)法糖,通過(guò)一個(gè)例子看一下generator的一般用法
let data = {
story: ['title1', 'title2', 'title3', 'title4', 'title5', 'title6'],
title1: '第一段段落昭娩,第一段段落第一段段落第一段段落第一段段落第一段段落第一段段落第一段段落第一段段落',
title2: '第二段段落凛篙,第二段段落第二段段落第二段段落第二段段落第二段段落第二段段落第二段段落第二段段落',
title3: '第三段段落,第三段段落第三段段落第三段段落第三段段落第三段段落第三段段落第三段段落第三段段落',
title4: '第四段段落栏渺,第四段段落第四段段落第四段段落第四段段落第四段段落第四段段落第四段段落第四段段落',
title5: '第五段段落呛梆,第五段段落第五段段落第五段段落第五段段落第五段段落第五段段落第五段段落第五段段落',
title6: '第六段段落,第六段段落第六段段落第六段段落第六段段落第六段段落第六段段落第六段段落第六段段落'
}
let time = {
story: 1000,
title1: 3000,
title2: 2000,
title3: 1000,
title4: 4000,
title5: 5000,
title6: 6000,
}
var getData = function (key) {
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(data[key])
}, time[key])
})
}
function co (generator) {
function handle (result) {
if (result.done) {
return
}
result.value.then((title)=>{
result = generator.next(title)
handle(result)
})
}
handle(generator.next())
}
function * main () {
const title1 = yield getData('title1')
console.log(title1)
const title2 = yield getData('title2')
console.log(title2)
const title3 = yield getData('title3')
console.log(title3)
const title4 = yield getData('title4')
console.log(title4)
const title5 = yield getData('title5')
console.log(title5)
const title6 = yield getData('title6')
console.log(title6)
}
co(main())
上面代碼中封裝了一個(gè)co函數(shù)迈嘹,用來(lái)專門(mén)處理生成器函數(shù)削彬,直接使用async/await可以代替co函數(shù)
async function main () {
const title1 = await getData('title1')
console.log(title1)
const title2 = await getData('title2')
console.log(title2)
const title3 = await getData('title3')
console.log(title3)
const title4 = await getData('title4')
console.log(title4)
const title5 = await getData('title5')
console.log(title5)
const title6 = await getData('title6')
console.log(title6)
}
const result = main()
result.then(data=>console.log('compolete'))
這里我們直接調(diào)用main函數(shù)全庸,無(wú)需借助co函數(shù)就可以實(shí)現(xiàn)上述邏輯,并且async會(huì)返回一個(gè)promise對(duì)象融痛,方便做統(tǒng)一處理壶笼。