宏任務(wù) 和 微任務(wù)

宏任務(wù):

當(dāng)前調(diào)用棧執(zhí)行的代碼成為宏任務(wù)诀黍,(主代碼塊和定時器)也或者宿主環(huán)境提供的叫宏任務(wù)

這些任務(wù)包括:

  • 渲染事件
  • 用戶交互事件(如鼠標(biāo)點擊、滾動頁面、放大縮小等)
  • JavaScript 腳本執(zhí)行事件酒唉;
  • 網(wǎng)絡(luò)請求完成墙基、文件讀寫完成事件

微任務(wù):

當(dāng)前(此次事件循環(huán)中)宏任務(wù)執(zhí)行完,在下一個宏任務(wù)開始之前需要執(zhí)行的任務(wù),可以理解為回調(diào)事件:promise.then,proness.nextTick等等。 由語言標(biāo)準(zhǔn)提供的叫微任務(wù).

執(zhí)行順序:

在掛起任務(wù)的時候添谊, JS 引擎會把任務(wù)按照類別分到兩個隊伍當(dāng)中财喳。 首先在宏任務(wù)(macrotask) 隊伍中取出第一個任務(wù),執(zhí)行完畢后斩狱。取出 microtask 隊列中的所有任務(wù)順序執(zhí)行耳高;周而復(fù)始,循環(huán)所踊。

總結(jié)上面:
可以舉個例子:就像銀行柜臺辦理業(yè)務(wù)泌枪,每個來辦理業(yè)務(wù)的人就像一個一個宏任務(wù),當(dāng)前用戶業(yè)務(wù)辦理完成然后 接待下一個客戶秕岛,就像開始了下一個宏任務(wù)一樣碌燕。但是一個客戶可能要辦理多項業(yè)務(wù)(修改密碼,存款继薛,轉(zhuǎn)賬等)這些業(yè)務(wù)就像微任務(wù)修壕,只有微任務(wù)執(zhí)行完成,才能執(zhí)行下一個宏任務(wù)(總不能一個客戶業(yè)務(wù)沒有辦理完惋增,就讓他去重新取號排隊叠殷,估計要打人了!U┟蟆A质)

setTimeout(function(){
    console.log('定時器開始啦')
});

new Promise(function(resolve){
    console.log('馬上執(zhí)行for循環(huán)啦');
    for(var i = 0; i < 10000; i++){
        i == 99 && resolve();
    }
}).then(function(){
    console.log('執(zhí)行then函數(shù)啦')
});

console.log('代碼執(zhí)行結(jié)束');

//馬上執(zhí)行for循環(huán)啦
//代碼執(zhí)行結(jié)束
//執(zhí)行then函數(shù)啦
//定時器開始啦
  • 這段代碼作為宏任務(wù)像棘,進(jìn)入主線程。
  • 先遇到setTimeout壶冒,那么將其回調(diào)函數(shù)注冊后分發(fā)到宏任務(wù)Event Queue缕题。(注冊過程與上同,下文不再描述)
  • 接下來遇到了Promise胖腾,new Promise立即執(zhí)行烟零,then函數(shù)分發(fā)到微任務(wù)Event Queue。
  • 遇到console.log()咸作,立即執(zhí)行锨阿。
  • 好啦,整體代碼script作為第一個宏任務(wù)執(zhí)行結(jié)束记罚,看看有哪些微任務(wù)墅诡?我們發(fā)現(xiàn)了then在微任務(wù)Event Queue里面,執(zhí)行桐智。
  • ok末早,第一輪事件循環(huán)結(jié)束了,我們開始第二輪循環(huán)说庭,當(dāng)然要從宏任務(wù)Event Queue開始然磷。我們發(fā)現(xiàn)了宏任務(wù)Event Queue中setTimeout對應(yīng)的回調(diào)函數(shù),立即執(zhí)行刊驴。
  • 結(jié)束

看下demo:

Promise 在前 setTimeout在后面

new Promise((resolve) => {
  console.log('外層宏事件2');
  resolve()
}).then(() => {
  console.log('微事件1');
}).then(()=>{
  console.log('微事件2')
})
console.log('外層宏事件1');
setTimeout(() => {
  //執(zhí)行后 回調(diào)一個宏事件
  console.log('內(nèi)層宏事件3')
}, 0)

// 執(zhí)行結(jié)果:
外層宏事件2
外層宏事件1
微事件1
微事件2
內(nèi)層宏事件3
  • Promise 在前姿搜,Promise.then則是具有代表性的微任務(wù),所有會進(jìn)入的異步都是指事件的回調(diào)捆憎。所以說 new Promise 在實例化的過程中所執(zhí)行的代碼都是同步進(jìn)行的痪欲,而then 中的才是異步執(zhí)行的
  • setTimeout就是作為宏任務(wù)來存在的
  • 在同步執(zhí)行完成之后,檢查是否有異步任務(wù)攻礼,微任務(wù)會在下一個宏任務(wù)前面全部完成
  • 所以 結(jié)果是 外2-外1-微1-微2-內(nèi)3

setTimeout 在前面 Promise 在后面

setTimeout(() => {
  //執(zhí)行后 回調(diào)一個宏事件
  console.log('內(nèi)層宏事件3')
}, 0)
console.log('外層宏事件1');

new Promise((resolve) => {
  console.log('外層宏事件2');
  resolve()
}).then(() => {
  console.log('微事件1');
}).then(()=>{
  console.log('微事件2')
})
// 執(zhí)行結(jié)果:
外層宏事件1
外層宏事件2
微事件1
微事件2
內(nèi)層宏事件3
  • setTimeout 設(shè)定了時間,相當(dāng)于取號了栗柒,在排隊過程礁扮。
  • 然后在當(dāng)前進(jìn)程中又添加了一些Promise的處理(臨時添加的業(yè)務(wù))
  • 同步的外1 和外2 執(zhí)行完成,開始執(zhí)行微任務(wù)瞬沦,微任務(wù)執(zhí)行完成之后才執(zhí)行下一個異步宏任務(wù)太伊,所以結(jié)果如上;

前面提到宿主環(huán)境:能夠使js 完美運行的環(huán)境逛钻,目前常見的環(huán)境就是兩種宿主環(huán)境有瀏覽器和node

console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
// 1-7 -6 -8 -2- 4- 3- 5- 9 -11- 10 -12

  • 整體script作為第一個宏任務(wù)進(jìn)入主線程僚焦,遇到console.log,輸出1曙痘。
  • 遇到setTimeout芳悲,其回調(diào)函數(shù)被分發(fā)到宏任務(wù)Event Queue中立肘。我們暫且記為setTimeout1。
  • 遇到process.nextTick()名扛,其回調(diào)函數(shù)被分發(fā)到微任務(wù)Event Queue中谅年。我們記為process1。
  • 遇到Promise肮韧,new Promise直接執(zhí)行融蹂,輸出7。then被分發(fā)到微任務(wù)Event Queue中弄企。我們記為then1超燃。
  • 又遇到了setTimeout,其回調(diào)函數(shù)被分發(fā)到宏任務(wù)Event Queue中拘领,我們記為setTimeout2意乓。
  • 同步輸出1-7 執(zhí)行微任務(wù) process1-then1 輸出6 - 8
  • 好了,第一輪事件循環(huán)正式結(jié)束院究,這一輪的結(jié)果是輸出1洽瞬,7,6业汰,8伙窃。那么第二輪時間循環(huán)從setTimeout1宏任務(wù)開始:
    • 首先輸出2。接下來遇到了process.nextTick()样漆,同樣將其分發(fā)到微任務(wù)Event Queue中为障,記為process2。new Promise立即執(zhí)行輸出4放祟,then也分發(fā)到微任務(wù)Event Queue中鳍怨,記為then2。
    • 第二輪事件循環(huán)宏任務(wù)結(jié)束跪妥,我們發(fā)現(xiàn)有process2和then2兩個微任務(wù)可以執(zhí)行鞋喇。 輸出 3和5
    • 第二輪事件循環(huán)結(jié)束,第二輪輸出2眉撵,4侦香,3,5纽疟。
    • 第三輪事件循環(huán)開始罐韩,此時只剩setTimeout2了,執(zhí)行污朽。
    • 輸出9散吵,11,10,12矾睦。

node 環(huán)境和瀏覽器環(huán)境 又有什么區(qū)別呢晦款?

  • 宏任務(wù)


    WechatIMG506.png
  • requestAnimationFrame姑且也算是宏任務(wù)吧,requestAnimationFrameMDN的定義為顷锰,下次頁面重繪前所執(zhí)行的操作柬赐,而重繪也是作為宏任務(wù)的一個步驟來存在的,且該步驟晚于微任務(wù)的執(zhí)行

  • 微任務(wù)

WechatIMG507.png

JS 是單線程官紫,所以同一個時間不能處理多個任務(wù)肛宋,所以每次辦理完一個業(yè)務(wù),都會詢問當(dāng)前客戶是否還有其他要辦理的業(yè)務(wù)(檢查是否有未完成的微任務(wù))束世,當(dāng)前用戶辦理完成酝陈,結(jié)束這個宏任務(wù)開始下一個宏任務(wù),這樣操作持續(xù)進(jìn)行毁涉,而這樣的操作就被稱為Event Loop

什么是Event Loop沉帮?

event loop 顧名思義 就是事件循環(huán)。因為V8是單線程的贫堰,即同一時間只能干一件事情穆壕,但是呢文件的讀取,網(wǎng)絡(luò)的IO處理是很緩慢的其屏,并且是不確定的,如果同步等待它們響應(yīng)喇勋,那么用戶就起飛了。于是我們就把這個事件加入到一個 事件隊列里(task),等到事件完成時偎行,event loop再執(zhí)行另一個事件隊列川背。

1、 update_time
在事件循環(huán)的開頭蛤袒,這一步的作用實際上是為了獲取一下系統(tǒng)時間

2熄云、timers
事件循環(huán)跑到這個階段的時候,要檢查是否有到期的timer,其實也就是setTimeout和setInterval這種類型的timer妙真,到期了缴允,就會執(zhí)行他們的回調(diào)。

3珍德、I/O callbacks
處理異步事件的回調(diào)癌椿,比如網(wǎng)絡(luò)I/O,比如文件讀取I/O菱阵。當(dāng)這些I/O動作都結(jié)束的時候,在這個階段會觸發(fā)它們的回調(diào)缩功。

4晴及、idle, prepare
這個階段內(nèi)部做一些動作,與理解事件循環(huán)沒啥關(guān)系

5嫡锌、I/O poll階段
這個階段相當(dāng)有意思虑稼,也是事件循環(huán)設(shè)計的一個有趣的點琳钉。這個階段是選擇運行的。選擇運行的意思就是不一定會運行蛛倦。

6歌懒、check
執(zhí)行setImmediate操作

7、close callbacks
關(guān)閉I/O的動作溯壶,比如文件描述符的關(guān)閉及皂,鏈接斷開

除了task還有一個microtask,這一個概念是ES6提出Promise以后出現(xiàn)的且改。這個microtask queue只有一個验烧。
并且會在且一定會在每一個task后執(zhí)行,且執(zhí)行是按順序的又跛。加入到microtask 的事件類型有Promise.resolve().then(), process.nextTick() 值得注意的是
event loop一定會在執(zhí)行完micrtask以后才會尋找新的 可執(zhí)行的task隊列碍拆。而microtask事件內(nèi)部又可以產(chǎn)生新的microtask

瀏覽器:

  • 宏任務(wù)(macroTask):script 中代碼、setTimeout慨蓝、setInterval感混、I/O、UI render
  • 微任務(wù)(microTask): Promise礼烈、Object.observe弧满、MutationObserver。
  • I/O 有點籠統(tǒng)济丘,點擊個btn 上傳一個文件谱秽,與程序交互的這些都可以稱為I/O

<style>
  #outer {
    padding: 20px;
    background: #616161;
  }

  #inner {
    width: 100px;
    height: 100px;
    background: #757575;
  }
</style>
<div id="outer">
  <div id="inner"></div>
</div>


const $inner = document.querySelector('#inner')
const $outer = document.querySelector('#outer')

function handler () {
  console.log('click') // 直接輸出

  Promise.resolve().then(_ => console.log('promise')) // 注冊微任務(wù)

  setTimeout(_ => console.log('timeout')) // 注冊宏任務(wù)

  requestAnimationFrame(_ => console.log('animationFrame')) // 注冊宏任務(wù)

  $outer.setAttribute('data-random', Math.random()) // DOM屬性修改,觸發(fā)微任務(wù)
}

new MutationObserver(_ => {
  console.log('observer')
}).observe($outer, {
  attributes: true
})

$inner.addEventListener('click', handler)
$outer.addEventListener('click', handler)

1摹迷、因為一次I/O創(chuàng)建了一個宏任務(wù)疟赊,也就是說在這次任務(wù)中會去觸發(fā)handler
2、在同步的代碼已經(jīng)執(zhí)行完以后峡碉,這時就會去查看是否有微任務(wù)可以執(zhí)行近哟,然后發(fā)現(xiàn)了Promise和MutationObserver兩個微任務(wù),遂執(zhí)行之
3鲫寄、click事件會冒泡吉执,所以對應(yīng)的這次I/O會觸發(fā)兩次handler函數(shù)(一次在inner、一次在outer)地来,所以會優(yōu)先執(zhí)行冒泡的事件(早于其他的宏任務(wù))戳玫,也就是說會重復(fù)上述的邏輯
4、在執(zhí)行完同步代碼與微任務(wù)以后未斑,這時繼續(xù)向后查找有木有宏任務(wù)
5咕宿、因為我們觸發(fā)了setAttribute,實際上修改了DOM的屬性,這會導(dǎo)致頁面的重繪府阀,而這個set的操作是同步執(zhí)行的缆镣,也就是說requestAnimationFrame的回調(diào)會早于setTimeout所執(zhí)行

所以上面 執(zhí)行順序是:click -> promise -> observer -> click -> promise -> observer -> animationFrame -> animationFrame -> timeout -> timeout

node:

  • Node也是單線程,但是在處理Event Loop上與瀏覽器稍微有些不同
setImmediate與setTimeout的區(qū)別:

在官方文檔中的定義试浙,setImmediate為一次Event Loop執(zhí)行完畢后調(diào)用董瞻。
setTimeout則是通過計算一個延遲時間后進(jìn)行執(zhí)行。

  • microTask:微任務(wù)田巴;

  • nextTick:process.nextTick钠糊;

  • timers:執(zhí)行滿足條件的 setTimeout 、setInterval 回調(diào)固额;

  • I/O callbacks:是否有已完成的 I/O 操作的回調(diào)函數(shù)眠蚂,來自上一輪的 poll 殘留;

  • poll:等待還沒完成的 I/O 事件斗躏,會因 timers 和超時時間等結(jié)束等待逝慧;

  • check:執(zhí)行 setImmediate 的回調(diào);

  • close callbacks:關(guān)閉所有的 closing handles 啄糙,一些 onclose 事件笛臣;
    idle/prepare 等等:可忽略。


  • macro-task(宏任務(wù)):包括整體代碼script隧饼,setTimeout沈堡,setInterval
  • micro-task(微任務(wù)):Promise,process.nextTick

進(jìn)程和線程的區(qū)別:

  • 線程是程序執(zhí)行的最小單位燕雁,而進(jìn)程是操作系統(tǒng)分配資源的最小單位诞丽;
  • 一個進(jìn)程由一個或多個線程組成,線程是一個進(jìn)程中代碼的不同執(zhí)行路線
  • 進(jìn)程之間相互獨立拐格,但同一進(jìn)程下的各個線程之間共享程序的內(nèi)存空間(包括代碼段僧免,數(shù)據(jù)集,堆等)及一些進(jìn)程級的資源(如打開文件和信號等)捏浊,某進(jìn)程內(nèi)的線程在其他進(jìn)程不可見懂衩;
  • 調(diào)度和切換:線程上下文切換比進(jìn)程上下文切換要快得多
  • 進(jìn)程是操作系統(tǒng)資源分配的基本單位,而線程是任務(wù)調(diào)度和執(zhí)行的基本單位
  • 內(nèi)存分配方面:
    系統(tǒng)在運行的時候會為每個進(jìn)程分配不同的內(nèi)存空間金踪;
    而對線程而言浊洞,除了CPU外,系統(tǒng)不會為線程分配內(nèi)存(線程所使用的資源來自其所屬進(jìn)程的資源)胡岔,線程組之間只能共享資源法希。
  • 所處環(huán)境:在操作系統(tǒng)中能同時運行多個進(jìn)程(程序);而在同一個進(jìn)程(程序)中有多個線程同時執(zhí)行(通過CPU調(diào)度靶瘸,在每個時間片中只有一個線程執(zhí)行)
  • 創(chuàng)建一個線程比進(jìn)程開銷刑摹尖淘;
  • 線程之間通信更方便,同一個進(jìn)程下著觉,線程共享全局變量,靜態(tài)變量等數(shù)據(jù)惊暴,進(jìn)程之間的通信需要以通信的方式(IPC)進(jìn)行饼丘;(但多線程程序處理好同步與互斥是個難點)

瀏覽器都有哪些進(jìn)程?

1.Browser進(jìn)程(即上篇文章截圖里面的瀏覽器進(jìn)程):瀏覽器的主進(jìn)程(負(fù)責(zé)協(xié)調(diào)辽话、主控)肄鸽,只有一個。主要作用:

  • 負(fù)責(zé)瀏覽器界面顯示油啤,與用戶交互典徘。如前進(jìn),后退等
  • 負(fù)責(zé)各個頁面的管理益咬,創(chuàng)建和銷毀其他進(jìn)程
  • 將渲染(Renderer)進(jìn)程得到的內(nèi)存中的Bitmap(位圖)逮诲,繪制到用戶界面上
  • 網(wǎng)絡(luò)資源的管理,下載等

2幽告、第三方插件進(jìn)程:每種類型的插件對應(yīng)一個進(jìn)程梅鹦,僅當(dāng)使用該插件時才創(chuàng)建
3、GPU進(jìn)程:最多一個冗锁,用于3D繪制等
4齐唆、瀏覽器渲染進(jìn)程(即通常所說的瀏覽器內(nèi)核)(Renderer進(jìn)程,內(nèi)部是多線程的):主要作用為頁面渲染冻河,腳本執(zhí)行箍邮,事件處理等

瀏覽器內(nèi)核

簡單來說瀏覽器內(nèi)核是通過取得頁面內(nèi)容、整理信息(應(yīng)用CSS)叨叙、計算和組合最終輸出可視化的圖像結(jié)果锭弊,通常也被稱為渲染引擎。從上面我們可以知道摔敛,Chrome瀏覽器為每個tab頁面單獨啟用進(jìn)程廷蓉,因此每個tab網(wǎng)頁都有由其獨立的渲染引擎實例

瀏覽器內(nèi)核是多線程,在內(nèi)核控制下各線程相互配合以保持同步马昙,一個瀏覽器通常由以下常駐線程組成

  • GUI 渲染線程

GUI渲染線程負(fù)責(zé)渲染瀏覽器界面HTML元素,當(dāng)界面需要重繪(Repaint)或由于某種操作引發(fā)回流(reflow)時,該線程就會執(zhí)行桃犬。在Javascript引擎運行腳本期間,GUI渲染線程都是處于掛起狀態(tài)的,也就是說被”凍結(jié)”了.

  • JavaScript引擎線程

Javascript引擎,也可以稱為JS內(nèi)核行楞,主要負(fù)責(zé)處理Javascript腳本程序攒暇,例如V8引擎。Javascript引擎線程理所當(dāng)然是負(fù)責(zé)解析Javascript腳本

  • 定時觸發(fā)器線程
  • 事件觸發(fā)線程
  • 異步http請求線程
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末子房,一起剝皮案震驚了整個濱河市形用,隨后出現(xiàn)的幾起案子就轧,更是在濱河造成了極大的恐慌,老刑警劉巖田度,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妒御,死亡現(xiàn)場離奇詭異,居然都是意外死亡镇饺,警方通過查閱死者的電腦和手機(jī)乎莉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奸笤,“玉大人惋啃,你說我怎么就攤上這事〖嘤遥” “怎么了边灭?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長健盒。 經(jīng)常有香客問我绒瘦,道長,這世上最難降的妖魔是什么味榛? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任椭坚,我火速辦了婚禮,結(jié)果婚禮上搏色,老公的妹妹穿的比我還像新娘善茎。我一直安慰自己,他們只是感情好频轿,可當(dāng)我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布垂涯。 她就那樣靜靜地躺著,像睡著了一般航邢。 火紅的嫁衣襯著肌膚如雪耕赘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天膳殷,我揣著相機(jī)與錄音操骡,去河邊找鬼。 笑死赚窃,一個胖子當(dāng)著我的面吹牛册招,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勒极,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼是掰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了辱匿?” 一聲冷哼從身側(cè)響起键痛,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤炫彩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后絮短,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體江兢,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年丁频,在試婚紗的時候發(fā)現(xiàn)自己被綠了划址。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡限府,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出痢缎,到底是詐尸還是另有隱情胁勺,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布独旷,位于F島的核電站署穗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嵌洼。R本人自食惡果不足惜案疲,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望麻养。 院中可真熱鬧褐啡,春花似錦、人聲如沸鳖昌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽许昨。三九已至懂盐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間糕档,已是汗流浹背莉恼。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留速那,地道東北人俐银。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像琅坡,于是被迫代替她去往敵國和親悉患。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,665評論 2 354

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