淺談不同環(huán)境下的JavaScript執(zhí)行機(jī)制 + 示例詳解

封面

概念

同步任務(wù)(Synchronous)

  • 在主線(xiàn)程上排隊(duì)執(zhí)行的任務(wù)胖腾,只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù)

異步任務(wù)(Asynchronous)

  • 不進(jìn)入主線(xiàn)程是己,而是進(jìn)入“任務(wù)隊(duì)列”的任務(wù)跟伏,只有主線(xiàn)執(zhí)行棧清空,異步任務(wù)才進(jìn)入主線(xiàn)執(zhí)行棧執(zhí)行

任務(wù)隊(duì)列(Task Queue)

  • 包含有異步任務(wù)的隊(duì)列销钝,包括“宏任務(wù)”與“微任務(wù)”

宏任務(wù)(Macrotasks / Task)

  • 創(chuàng)建文檔對(duì)象有咨、解析 HTML琐簇、執(zhí)行主線(xiàn)程代碼(script)
  • 執(zhí)行各種事件:頁(yè)面加載、輸入座享、點(diǎn)擊
  • setTimout婉商,setIntervalsetImmediate
  • I/O渣叛,Ajax丈秩,UI rendering

微任務(wù)(Microtasks / Jobs)

  • process.nextTick
  • Promise.then
  • Object.observe(已廢棄)
  • MutationObserver

事件循環(huán)(Event Loop)

瀏覽器下的事件循環(huán)
  • 事件循環(huán)是js實(shí)現(xiàn)異步的一種方法,也是js的執(zhí)行機(jī)制
  • JavaScript 主線(xiàn)程會(huì)在執(zhí)行棧清空后淳衙,讀取任務(wù)隊(duì)列蘑秽,入棧第一個(gè)宏任務(wù)饺著,主線(xiàn)程執(zhí)行完該任務(wù)后又會(huì)先檢查微任務(wù)隊(duì)列并完成里面的所有微任務(wù)积瞒,包括新創(chuàng)建的微任務(wù)饵较,完成一次事件循環(huán)。之后再次去讀取任務(wù)隊(duì)列诊霹,不斷循環(huán)
  • 注意:
    • 每次循環(huán)只會(huì)入棧一個(gè)宏任務(wù)缀雳,所以多個(gè)宏任務(wù)需要多次事件循環(huán)才能執(zhí)行完
    • 每次循環(huán)會(huì)執(zhí)行所有的微任務(wù)渡嚣,所以每次循環(huán)結(jié)束后微任務(wù)隊(duì)列被清空
Node.JS下的事件循環(huán)
  1. timers
    • 執(zhí)行 setTimeoutsetInterval 中到期的 callback
  2. I/O callbacks
    • 除了以下操作的回調(diào)函數(shù),其他的回調(diào)函數(shù)都在這個(gè)階段執(zhí)行肥印。
      • setTimeout识椰,setIntervalsetImmediatecallback
      • 用于執(zhí)行 close 事件(關(guān)閉請(qǐng)求)的 callback深碱,例如 socket.on('close', callback)
  3. idle, prepare
    • libuv 內(nèi)部調(diào)用
  4. poll
    • 最為重要的階段腹鹉,用于等待還未返回的 I/O 事件,比如服務(wù)器的回應(yīng)敷硅、用戶(hù)移動(dòng)鼠標(biāo)等等
    • 這個(gè)階段的時(shí)間會(huì)比較長(zhǎng)种蘸。如果沒(méi)有其他異步任務(wù)要處理(比如到期的定時(shí)器),會(huì)一直停留在這個(gè)階段竞膳,等待 I/O 請(qǐng)求返回結(jié)果航瞭。
  5. check
    • 執(zhí)行 setImmediatecallback
  6. close callbacks
    • 執(zhí)行 close 事件(關(guān)閉請(qǐng)求)的 callback,例如 socket.on('close', callback)
  • 事件循環(huán)的每一次循環(huán)都需要依次經(jīng)過(guò)上述的階段坦辟。 每個(gè)階段都有自己的 callback 隊(duì)列刊侯,每當(dāng)進(jìn)入某個(gè)階段,都會(huì)從所屬的隊(duì)列中取出callback來(lái)執(zhí)行锉走,當(dāng)隊(duì)列為空或者被執(zhí)行callback的數(shù)量達(dá)到系統(tǒng)的最大數(shù)量時(shí)滨彻,進(jìn)入下一階段。這六個(gè)階段都執(zhí)行完畢稱(chēng)為一輪循環(huán)
  • 注意:
    • 不同于瀏覽器的是挪蹭,在每個(gè)階段完成后亭饵,microTask隊(duì)列就會(huì)被執(zhí)行,而不是MacroTask任務(wù)完成后梁厉。
    • 每個(gè)階段完成后辜羊,微任務(wù)隊(duì)列就會(huì)被執(zhí)行。
    • 如果在timers階段執(zhí)行時(shí)創(chuàng)建了setImmediate則會(huì)在此輪循環(huán)的check階段執(zhí)行词顾,如果在timers階段創(chuàng)建了 setTimeout八秃,由于timers已取出完畢,則會(huì)進(jìn)入下輪循環(huán)肉盹,check階段創(chuàng)建timers任務(wù)同理
    • 遞歸的調(diào)用 process.nextTick 會(huì)導(dǎo)致 I/O starving昔驱,官方推薦使用 setImmediate
Node.JS與瀏覽器下的差異
  • 瀏覽器環(huán)境下,microtask 的任務(wù)隊(duì)列是每個(gè) macrotask 執(zhí)行完之后執(zhí)行
  • Node.js中上忍,microtask 會(huì)在事件循環(huán)的各個(gè)階段之間執(zhí)行骤肛,也就是一個(gè)階段執(zhí)行完畢纳本,就會(huì)去執(zhí)行 microtask 隊(duì)列的任務(wù)
setTimeout(()=>{
    console.log('timer1')

    Promise.resolve().then(function() {
        console.log('promise1')
    })
}, 0)

setTimeout(()=>{
    console.log('timer2')

    Promise.resolve().then(function() {
        console.log('promise2')
    })
}, 0)

// 瀏覽器輸出:
// time1
// promise1
// time2
// promise2

// Node輸出:
// time1
// time2
// promise1
// promise2

定時(shí)器

setTimeout(callback, time)

  • 經(jīng)過(guò)指定時(shí)間后,把要執(zhí)行的任務(wù) callback 加入到任務(wù)隊(duì)列中
  • 因?yàn)镴S是單線(xiàn)程腋颠,任務(wù)要一個(gè)一個(gè)執(zhí)行饮醇,如果前面的任務(wù)需要的時(shí)間太久,那么只能等著秕豫,導(dǎo)致真正的延遲時(shí)間可能遠(yuǎn)遠(yuǎn)大于指定時(shí)間(time ms)

setTimeout(callback, 0)

  • 指定某個(gè)任務(wù) callback 在主線(xiàn)程最早可得的空閑時(shí)間執(zhí)行朴艰,意思就是不用再等多少秒了,只要主線(xiàn)程執(zhí)行棧內(nèi)的同步任務(wù)全部執(zhí)行完成混移,棧為空就馬上執(zhí)行
  • 0ms 實(shí)際上是不可能的祠墅,在瀏覽器中 setTimeout() / setInterval() 的每調(diào)用一次定時(shí)器的最小間隔 >=4ms,這通常是由于函數(shù)嵌套導(dǎo)致(嵌套層級(jí)達(dá)到一定深度)歌径,或者是由于已經(jīng)執(zhí)行的 setInterval 的回調(diào)函數(shù)阻塞導(dǎo)致的
  • 在 Node.JS 環(huán)境為 1ms毁嗦,但也取決于系統(tǒng)當(dāng)時(shí)的狀況
setTimeout(function () {
    console.log("1");
}, 0)

console.log(2)

// 輸出 2 1

setInterval(callback, time)

  • 每過(guò)指定時(shí)間(time ms),會(huì)有 callback 進(jìn)入任務(wù)隊(duì)列回铛。
  • callback 執(zhí)行時(shí)間超過(guò)了指定時(shí)間狗准,那么就會(huì)導(dǎo)致 callback 連續(xù)執(zhí)行,完全看不出來(lái)有時(shí)間間隔了

setImmediate(callback)

  • Node.JS 特有定時(shí)器茵肃,在事件循環(huán)的 check 階段執(zhí)行

process.nextTick(callback)

  • Node.JS 特有定時(shí)器腔长,在事件循環(huán)各個(gè)階段結(jié)束后執(zhí)行
  • 從技術(shù)上講,它不是事件循環(huán)的一部分
  • 同循環(huán)下 process.nextTick 會(huì)優(yōu)于 Promise.then
Promise.resolve().then(() => console.log(1));
process.nextTick(() => console.log(2));

// 輸出 2 1

注意

  • 連續(xù)的 setTimeout验残,setImmediate 在再 timer 階段的執(zhí)行順序是不確定的捞附,取決于系統(tǒng)當(dāng)時(shí)的狀況
  • 但是把 setTimeoutsetImmediate 放到一個(gè) I/O 回調(diào)里面您没,就一定是 setImmediate 先執(zhí)行鸟召,因?yàn)?poll 階段后面就是 check 階段
setImmediate(() => {
  console.log('timer1')

  Promise.resolve().then(function () {
    console.log('promise1')
  })
})

setTimeout(() => {
  console.log('timer2')

  Promise.resolve().then(function () {
    console.log('promise2')
  })
}, 0)

// Node輸出:
// timer1               timer2
// promise1    或者     promise2
// timer2               timer1
// promise2             promise1
fs.readFile('test.js', () => {
  setTimeout(() => console.log(1));
  setImmediate(() => console.log(2));
})

// 輸出 2 1

// 先進(jìn)入 I/O callbacks 階段,然后是 check 階段氨鹏,最后才是 下一次事件循環(huán)的 timers 階段欧募。因此,setImmediate 才會(huì)早于setTimeout 執(zhí)行仆抵。

示例

console.log(0)

new Promise(function(resolve) {
    console.log(1);
    resolve();
}).then(function() {
    console.log(2)
})

setTimeout(function() {
    console.log(3);
    new Promise(function(resolve) {
        console.log(4);
        resolve();
    }).then(function() {
        console.log(5)
    })
})

new Promise(function(resolve) {
    console.log(6);
    resolve();
}).then(function() {
    console.log(7)
})

setTimeout(function() {
    console.log(8);
    new Promise(function(resolve) {
        console.log(9);
        resolve();
    }).then(function() {
        console.log(10)
    })
})

console.log(11)

瀏覽器環(huán)境

第一輪事件循環(huán)
  • 第一輪事件循環(huán)宏任務(wù)
  1. 開(kāi)始執(zhí)行代碼跟继,遇到 console.log 輸出 0
  2. 接著遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
    1. 遇到 console.log 輸出 1
    2. 遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
  3. 接著遇到 setTimeout 其回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“宏任務(wù)隊(duì)列”
  4. 接著遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
    1. 遇到 console.log 輸出 6
    2. 遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
  5. 接著遇到 setTimeout 其回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“宏任務(wù)隊(duì)列”
  6. 最后遇到 console.log 輸出 11
  7. 此時(shí)第一輪事件循環(huán)宏任務(wù)結(jié)束,依次輸出 0 1 6 11
  • 第一輪事件循環(huán)微任務(wù)
  1. 執(zhí)行注冊(cè)在“微任務(wù)隊(duì)列”里的微任務(wù)肢础,遇到 then 執(zhí)行其回調(diào)輸出 2
  2. 遇到 then 執(zhí)行其回調(diào)輸出 7
  3. 此時(shí)第一輪事件循環(huán)微任務(wù)結(jié)束还栓,依次輸出 2 7
  • 第一輪事件循環(huán)結(jié)束,此時(shí)“微任務(wù)隊(duì)列”已被清空传轰,“宏任務(wù)隊(duì)列”里有兩個(gè) setTimeout 的回調(diào)函數(shù),第一個(gè) setTimeout 被送入主線(xiàn)程執(zhí)行棧
第二輪事件循環(huán)
  • 第二輪事件循環(huán)宏任務(wù)
  1. 開(kāi)始執(zhí)行第一個(gè) setTimeout 回調(diào)函數(shù)
    1. 遇到 console.log 輸出 3
    2. 遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
      1. 遇到 console.log 輸出 4
      2. 遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
  2. 此時(shí)第二輪事件循環(huán)宏任務(wù)結(jié)束谷婆,依次輸出 3 4
  • 第二輪事件循環(huán)微任務(wù)
  1. 執(zhí)行注冊(cè)在“微任務(wù)隊(duì)列”里的微任務(wù)慨蛙,遇到 then 執(zhí)行其回調(diào)輸出 5
  2. 此時(shí)第二輪事件循環(huán)微任務(wù)結(jié)束辽聊,輸出 5
  • 第二輪事件循環(huán)結(jié)束,此時(shí)“微任務(wù)隊(duì)列”已被清空期贫,“宏任務(wù)隊(duì)列”里剩下一個(gè) setTimeout 的回調(diào)函數(shù)跟匆,其被送入主線(xiàn)程執(zhí)行棧
第三輪事件循環(huán)
  • 第三輪事件循環(huán)宏任務(wù)
  1. 開(kāi)始執(zhí)行第二個(gè) setTimeout 回調(diào)函數(shù)
    1. 遇到 console.log 輸出 8
    2. 遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
      1. 遇到 console.log 輸出 9
      2. 遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
  2. 此時(shí)第三輪事件循環(huán)宏任務(wù)結(jié)束,依次輸出 8 9
  • 第三輪事件循環(huán)微任務(wù)
  1. 執(zhí)行注冊(cè)在“微任務(wù)隊(duì)列”里的微任務(wù)通砍,遇到 then 執(zhí)行其回調(diào)輸出 10
  2. 此時(shí)第三輪事件循環(huán)微任務(wù)結(jié)束玛臂,輸出 10
  • 第三輪事件循環(huán)結(jié)束,此時(shí)“微任務(wù)隊(duì)列”已被清空封孙,“宏任務(wù)隊(duì)列”已被清空

至此整段代碼執(zhí)行完畢迹冤,完整輸出結(jié)果為:0 1 6 11 2 7 3 4 5 8 9 10

Node.JS 環(huán)境

Node.JS 環(huán)境下任務(wù)隊(duì)列有層級(jí)之分,按層級(jí)執(zhí)行任務(wù)隊(duì)列

第一輪事件循環(huán)
  • 第一輪事件循環(huán)宏任務(wù)
  1. 開(kāi)始執(zhí)行代碼虎忌,遇到 console.log 輸出 0
  2. 接著遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
    1. 遇到 console.log 輸出 1
    2. 遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
  3. 接著遇到 setTimeout 其回調(diào)函數(shù)注冊(cè)到 timer 階段
  4. 接著遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
    1. 遇到 console.log 輸出 6
    2. 遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
  5. 接著遇到 setTimeout 其回調(diào)函數(shù)注冊(cè)到 timer 階段
  6. 最后遇到 console.log 輸出 11
  7. 此時(shí)第一輪事件循環(huán)宏任務(wù)結(jié)束泡徙,依次輸出 0 1 6 11
  • 第一輪事件循環(huán)微任務(wù)
  1. 執(zhí)行注冊(cè)在“微任務(wù)隊(duì)列”里的微任務(wù),遇到 then 執(zhí)行其回調(diào)輸出 2
  2. 遇到 then 執(zhí)行其回調(diào)輸出 7
  3. 此時(shí)第一輪事件循環(huán)微任務(wù)結(jié)束膜蠢,依次輸出 2 7
  • 第一輪事件循環(huán)結(jié)束堪藐,此時(shí)“微任務(wù)隊(duì)列”已被清空,timer 隊(duì)列里有兩個(gè) setTimeout 的回調(diào)函數(shù)
第二輪事件循環(huán)
  • 第二輪事件循環(huán) timer 階段
  1. 執(zhí)行第一個(gè) setTimeout 回調(diào)函數(shù)
    1. 遇到 console.log 輸出 3
    2. 遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
      1. 遇到 console.log 輸出 4
      2. 遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
  2. 執(zhí)行第二個(gè) setTimeout 回調(diào)函數(shù)
    1. 遇到 console.log 輸出 8
    2. 遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
      1. 遇到 console.log 輸出 9
      2. 遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
  3. 此時(shí)第二輪事件循 timer 階段結(jié)束挑围,依次輸出 3 4 8 9
  • 第二輪事件循環(huán) timer 階段微任務(wù)
  1. 執(zhí)行注冊(cè)在“微任務(wù)隊(duì)列”里的微任務(wù)
  2. 遇到 then 執(zhí)行其回調(diào)輸出 5
  3. 遇到 then 執(zhí)行其回調(diào)輸出 10
  4. 此時(shí)第二輪事件循環(huán) timer 階段微任務(wù)結(jié)束礁竞,輸出 5 10
  • 第二輪事件循環(huán)結(jié)束,至此整段代碼執(zhí)行完畢杉辙,完整輸出結(jié)果為:0 1 6 11 2 7 3 4 8 9 5 10

參考文章

阮一峰 - JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop

阮一峰 - Node 定時(shí)器詳解

Philip Roberts - Help,I’m stuck in an event loop

lynnelv - 深入理解js事件循環(huán)機(jī)制(Node.js篇)

這一次苏章,徹底弄懂 JavaScript 執(zhí)行機(jī)制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奏瞬,隨后出現(xiàn)的幾起案子枫绅,更是在濱河造成了極大的恐慌,老刑警劉巖硼端,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件并淋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡珍昨,警方通過(guò)查閱死者的電腦和手機(jī)县耽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)镣典,“玉大人兔毙,你說(shuō)我怎么就攤上這事⌒执海” “怎么了澎剥?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)赶舆。 經(jīng)常有香客問(wèn)我哑姚,道長(zhǎng)祭饭,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任叙量,我火速辦了婚禮倡蝙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绞佩。我一直安慰自己寺鸥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布品山。 她就那樣靜靜地躺著胆建,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谆奥。 梳的紋絲不亂的頭發(fā)上眼坏,一...
    開(kāi)封第一講書(shū)人閱讀 52,696評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音酸些,去河邊找鬼宰译。 笑死,一個(gè)胖子當(dāng)著我的面吹牛魄懂,可吹牛的內(nèi)容都是我干的沿侈。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼市栗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼缀拭!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起填帽,我...
    開(kāi)封第一講書(shū)人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蛛淋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后篡腌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體褐荷,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年嘹悼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叛甫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杨伙,死狀恐怖其监,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情限匣,我是刑警寧澤抖苦,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響睛约,放射性物質(zhì)發(fā)生泄漏鼎俘。R本人自食惡果不足惜哲身,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一辩涝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧勘天,春花似錦怔揩、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至宠进,卻和暖如春晕拆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背材蹬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工实幕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人堤器。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓昆庇,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親闸溃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子整吆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容