瀏覽器的多線程機(jī)制與Javascript EventLoop

標(biāo)簽:Event Loop iplas


瀏覽器多線程機(jī)制介紹

瀏覽網(wǎng)頁相信對于大家來說已經(jīng)是一件習(xí)以為常的事情了,那么在開始今天的分享之前恼琼,先賣個(gè)關(guān)子。你們知道瀏覽器是怎么渲染頁面的嗎谎碍?它是多線程的嗎萄喳?

在這里科普一下,瀏覽器確實(shí)是多線程的猾愿。請看下圖鹦聪,瀏覽器至少有以下現(xiàn)成組成:


image

瀏覽器頁面渲染流程

瀏覽器從HTTP服務(wù)器獲取html文檔,到呈現(xiàn)頁面給用戶蒂秘,會經(jīng)過以下幾個(gè)步驟:

1泽本、解析文檔構(gòu)建DOM樹

瀏覽器的解析內(nèi)容可以分為三個(gè)部分

  • HTML/XHTML/SVG:解析這三種文件后,會生成DOM樹(DOM Tree)

  • CSS:解析樣式表材彪,生成CSS規(guī)則樹(CSS Rule Tree)

  • JavaScript:解析腳本观挎,通過DOM API和CSSOM API操作DOM Tree和CSS Rule Tree,與用戶進(jìn)行交互段化。

2、構(gòu)建渲染樹

解析文檔完成后造成,瀏覽器引擎會將 CSS Rule Tree 附著到DOM Tree 上显熏,并根據(jù)DOM Tree 和 CSS Rule Tree構(gòu)造 Rendering Tree(渲染樹)。

3晒屎、布局與繪制渲染樹

解析position, overflow, z-index等等屬性喘蟆,計(jì)算每一個(gè)渲染樹節(jié)點(diǎn)的位置和大小,此過程被稱為reflow鼓鲁。最后調(diào)用操作系統(tǒng)的Native GUI API完成繪制(repain)蕴轨。

講到這里,可能有些同學(xué)會有疑惑骇吭,在印象中橙弱,瀏覽器明明是單線程的呀,怎么又變成多線程了?我們要區(qū)分一下概念棘脐,單線程處理的是js引擎斜筐,不是瀏覽器。在任何時(shí)候蛀缝,js引擎都是單線程的顷链。且需要注意 GUI渲染線程與JS引擎是互斥的,當(dāng)JS引擎執(zhí)行時(shí)GUI線程會被掛起屈梁。

舉個(gè)例子嗤练,編寫一個(gè)簡單的html頁面如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script type="text/javascript" src="defer.js"></script>
</head>
<body>
    body render!
</body>
</html>

head中加載的腳本文件內(nèi)容為:

alert('i am the script');

在瀏覽器中運(yùn)行時(shí),會先彈出提示框在讶,此時(shí)潭苞,body元素并沒有進(jìn)行渲染。


B6235C9C-CACE-475A-A276-3B2E013D89C9.png

關(guān)閉彈出框以后真朗,才完成對body的渲染
EF9C99B0-4BBE-46D0-82E9-B413C40D189F.png

我們經(jīng)常在瀏覽網(wǎng)頁時(shí)候此疹,經(jīng)常會遇到一個(gè)情況,顯示空白加載不出東西來遮婶,或是在點(diǎn)擊請求時(shí)候一直loading個(gè)不停蝗碎。可能某些人沒有遇到旗扑,反正小編是遇到了不少蹦骑,這讓小編很是郁悶。這是為什么呢臀防?打開瀏覽器控制臺一看才發(fā)現(xiàn)眠菇,原來是js運(yùn)行報(bào)錯(cuò)了,導(dǎo)致頁面渲染停止袱衷。這是一個(gè)很不友好的情況捎废,這也是各種建議js放在尾部加載而不在頭部加載的原因了,至少它可以先讓頁面渲染出來致燥。

這個(gè)時(shí)候可能又有人問登疗,我經(jīng)常在js里面編寫各種異步請求的代碼,它如果是單線程的嫌蚤,又是怎么處理異步請求的呢辐益?

不要急,不要慌脱吱,接下來才開始本次分享的重點(diǎn)講解智政。

JavaScriptsetTimeout與setInterval是兩個(gè)很容易欺騙別人感情的方法,因?yàn)槲覀冮_始常常以為調(diào)用了就會按既定的方式執(zhí)行, 我想不少人都深有同感, 例如

setTimeout( function(){ alert(’你好!’); } , 0);  
setInterval( callbackFunction , 100);  

執(zhí)行這兩個(gè)語句會發(fā)生什么呢?箱蝠?续捂?

相信不少人跟小編一樣認(rèn)為setTimeout中的問候方法會立即被執(zhí)行,因?yàn)檫@并不是憑空而說,而是JavaScript API文檔明確定義第二個(gè)參數(shù)意義為隔多少毫秒后,回調(diào)方法就會被執(zhí)行. 這里設(shè)成0毫秒,理所當(dāng)然就立即被執(zhí)行了.
同理對setInterval的callbackFunction方法每間隔100毫秒就立即被執(zhí)行深信不疑!

但隨著JavaScript應(yīng)用開發(fā)經(jīng)驗(yàn)不斷的增加和豐富,有一天你發(fā)現(xiàn)了一段怪異的代碼而百思不得其解:

div.onclick = function(){  
    setTimeout( function(){document.getElementById(’inputField’).focus();}, 0);  
};  

既然是0毫秒后執(zhí)行,那么還用setTimeout干什么, 此刻, 堅(jiān)定的信念已開始動(dòng)搖垦垂。

直到最后某一天 , 你不小心寫了一段糟糕的代碼:

setTimeout( function(){ while(true){} } , 100);  
setTimeout( function(){ alert(’你好!’); } , 200);  
setInterval( callbackFunction , 200);  

第一行代碼不出所料的進(jìn)入了死循環(huán),但很快你就會發(fā)現(xiàn),第二、第三行代碼并沒有如意料中的執(zhí)行下去疾忍,,alert問候未見出現(xiàn),callbacKFunction也杳無音訊!小編心里著急啊乔外,這是為什么呢?

真相只有一個(gè)

出現(xiàn)上面所有誤區(qū)的最主要一個(gè)原因是:潛意識中認(rèn)為,JavaScript引擎有多個(gè)線程在執(zhí)行,JavaScript的定時(shí)器回調(diào)函數(shù)是異步執(zhí)行的.

而事實(shí)上,JavaScript使用了障眼法,在多數(shù)時(shí)候騙過了我們的眼睛,這里背光得澄清一個(gè)事實(shí):
JavaScript引擎是單線程運(yùn)行的,瀏覽器無論在什么時(shí)候都只且只有一個(gè)線程在運(yùn)行JavaScript程序.

JavaScript引擎用單線程運(yùn)行也是有意義的,單線程不必理會線程同步這些復(fù)雜的問題,問題得到簡化一罩。
那么單線程的JavaScript引擎是怎么配合瀏覽器內(nèi)核處理這些定時(shí)器和響應(yīng)瀏覽器事件的呢?

下面結(jié)合瀏覽器內(nèi)核處理方式簡單說明.

瀏覽器內(nèi)核實(shí)現(xiàn)允許多個(gè)線程異步執(zhí)行,這些線程在內(nèi)核制控下相互配合以保持同步.假如某一瀏覽器內(nèi)核的實(shí)現(xiàn)至少有三個(gè)常駐線程:javascript引擎線程,界面渲染線程,瀏覽器事件觸發(fā)線程,除些以外,也有一些執(zhí)行完就終止的線程,如Http請求線程,這些異步線程都會產(chǎn)生不同的異步事件,下面通過一個(gè)圖來闡明單線程的JavaScript引擎與另外那些線程是怎樣互動(dòng)通信的.雖然每個(gè)瀏覽器內(nèi)核實(shí)現(xiàn)細(xì)節(jié)不同,但這其中的調(diào)用原理都是大同小異.


image

由圖可看出,瀏覽器中的JavaScript引擎是基于事件驅(qū)動(dòng)的,這里的事件可看作是瀏覽器派給它的各種任務(wù),這些任務(wù)可以源自JavaScript引擎當(dāng)前執(zhí)行的代碼塊,如調(diào)用setTimeout添加一個(gè)任務(wù),也可來自瀏覽器內(nèi)核的其它線程,如界面元素鼠標(biāo)點(diǎn)擊事件,定時(shí)觸發(fā)器時(shí)間到達(dá)通知,異步請求狀態(tài)變更通知等.從代碼角度看來任務(wù)實(shí)體就是各種回調(diào)函數(shù),JavaScript引擎一直等待著任務(wù)隊(duì)列中任務(wù)的到來.由于單線程關(guān)系,這些任務(wù)得進(jìn)行排隊(duì),一個(gè)接著一個(gè)被引擎處理.

上圖t1-t2..tn表示不同的時(shí)間點(diǎn),tn下面對應(yīng)的小方塊代表該時(shí)間點(diǎn)的任務(wù),假設(shè)現(xiàn)在是t1時(shí)刻,引擎運(yùn)行在t1對應(yīng)的任務(wù)方塊代碼內(nèi),在這個(gè)時(shí)間點(diǎn)內(nèi),我們來描述一下瀏覽器內(nèi)核其它線程的狀態(tài).

t1時(shí)刻:

GUI渲染線程:

該線程負(fù)責(zé)渲染瀏覽器界面HTML元素,當(dāng)界面需要重繪(Repaint)或由于某種操作引發(fā)回流(reflow)時(shí),該線程就會執(zhí)行.本文雖然重點(diǎn)解釋JavaScript定時(shí)機(jī)制,但這時(shí)有必要說說渲染線程,因?yàn)樵摼€程與JavaScript引擎線程是互斥的,這容易理解,因?yàn)镴avaScript腳本是可操縱DOM元素,在修改這些元素屬性同時(shí)渲染界面,那么渲染線程前后獲得的元素?cái)?shù)據(jù)就可能不一致了.
在JavaScript引擎運(yùn)行腳本期間,瀏覽器渲染線程都是處于掛起狀態(tài)的,也就是說被”凍結(jié)”了杨幼。

所以,在腳本中執(zhí)行對界面進(jìn)行更新操作,如添加結(jié)點(diǎn),刪除結(jié)點(diǎn)或改變結(jié)點(diǎn)的外觀等更新并不會立即體現(xiàn)出來,這些操作將保存在一個(gè)隊(duì)列中,待JavaScript引擎空閑時(shí)才有機(jī)會渲染出來.

GUI事件觸發(fā)線程:

JavaScript腳本的執(zhí)行不影響html元素事件的觸發(fā),在t1時(shí)間段內(nèi),首先是用戶點(diǎn)擊了一個(gè)鼠標(biāo)鍵,點(diǎn)擊被瀏覽器事件觸發(fā)線程捕捉后形成一個(gè)鼠標(biāo)點(diǎn)擊事件,由圖可知,對于JavaScript引擎線程來說,這事件是由其它線程異步傳到任務(wù)隊(duì)列尾的,由于引擎正在處理t1時(shí)的任務(wù),這個(gè)鼠標(biāo)點(diǎn)擊事件正在等待處理.

定時(shí)觸發(fā)線程:

注意這里的瀏覽器模型定時(shí)計(jì)數(shù)器并不是由JavaScript引擎計(jì)數(shù)的,因?yàn)镴avaScript引擎是單線程的,如果處于阻塞線程狀態(tài)就計(jì)不了時(shí),它必須依賴外部來計(jì)時(shí)并觸發(fā)定時(shí),所以隊(duì)列中的定時(shí)事件也是異步事件.

由圖可知,在這t1的時(shí)間段內(nèi),繼鼠標(biāo)點(diǎn)擊事件觸發(fā)后,先前已設(shè)置的setTimeout定時(shí)也到達(dá)了,此刻對JavaScript引擎來說,定時(shí)觸發(fā)線程產(chǎn)生了一個(gè)異步定時(shí)事件并放到任務(wù)隊(duì)列中, 該事件被排到點(diǎn)擊事件回調(diào)之后,等待處理.
同理, 還是在t1時(shí)間段內(nèi),接下來某個(gè)setInterval定時(shí)器也被添加了,由于是間隔定時(shí),在t1段內(nèi)連續(xù)被觸發(fā)了兩次,這兩個(gè)事件被排到隊(duì)尾等待處理。
可見,假如時(shí)間段t1非常長,遠(yuǎn)大于setInterval的定時(shí)間隔,那么定時(shí)觸發(fā)線程就會源源不斷的產(chǎn)生異步定時(shí)事件并放到任務(wù)隊(duì)列尾而不管它們是否已被處理,但一旦t1和最先的定時(shí)事件前面的任務(wù)已處理完,這些排列中的定時(shí)事件就依次不間斷的被執(zhí)行,這是因?yàn)?對于JavaScript引擎來說,在處理隊(duì)列中的各任務(wù)處理方式都是一樣的,只是處理的次序不同而已.

t1過后,也就是說當(dāng)前處理的任務(wù)已返回,JavaScript引擎會檢查任務(wù)隊(duì)列,發(fā)現(xiàn)當(dāng)前隊(duì)列非空,就取出t2下面對應(yīng)的任務(wù)執(zhí)行,其它時(shí)間依此類推,由此看來:
如果隊(duì)列非空,引擎就從隊(duì)列頭取出一個(gè)任務(wù),直到該任務(wù)處理完,即返回后引擎接著運(yùn)行下一個(gè)任務(wù),在任務(wù)沒返回前隊(duì)列中的其它任務(wù)是沒法被執(zhí)行的.

講到這里聂渊,稍微有點(diǎn)兒清晰了差购?
setTimeout、setInterval方法在執(zhí)行的時(shí)候汉嗽,觸發(fā)生成了一個(gè)Event(事件)到任務(wù)隊(duì)列中列中欲逃,Javascript引擎有序的執(zhí)行。

深入了解

現(xiàn)在我們已經(jīng)知道了Javascript引擎是基于事件驅(qū)動(dòng)的單線程工作引擎饼暑,用戶的各類操作行為(包括單擊稳析、雙擊、滾動(dòng)等)弓叛,setTimeout彰居、setInterval定時(shí)任務(wù)以及ajax異步請求等都是觸發(fā)了事件在任務(wù)隊(duì)列中被有序執(zhí)行,那么撰筷,這些事件都是一樣的么陈惰?

接下來,我們繼續(xù)看一段代碼:

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

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

console.log('script end');

這段代碼的執(zhí)行結(jié)果是怎樣的呢毕籽?

答案是:script start, script end, promise1, promise2, setTimeout
為啥是這樣的結(jié)果抬闯?看起來有點(diǎn)二讓人懵逼。

這是為什么呢关筒?

要理解這些你首先需要對事件循環(huán)機(jī)制處理宏任務(wù)和微任務(wù)的方式有了解溶握。
如果是第一次接觸信息量會有點(diǎn)大。深呼吸……

因?yàn)镴avascript 在瀏覽器端運(yùn)行是單線程的平委,稱之為主線程(HTML5提供了web worker API可以讓瀏覽器開一個(gè)線程運(yùn)行比較復(fù)雜耗時(shí)的 javascript任務(wù)奈虾,但是這個(gè)線程仍受主線程的控制)。如果我們做一些“sleep”的操作比如說

var now = + new Date()
while (+new Date() <= now + 1000){
//這是一個(gè)耗時(shí)的操所
}

那么在這將近一秒內(nèi)廉赔,線程就會被阻塞,無法繼續(xù)執(zhí)行下面的任務(wù)匾鸥。
還有些操作比如說獲取遠(yuǎn)程數(shù)據(jù)蜡塌、I/O操作等,他們都很耗時(shí)勿负,如果采用同步的方式馏艾,那么進(jìn)程在執(zhí)行這些操作時(shí)就會因?yàn)楹臅r(shí)而等待劳曹,就像上面那樣,下面的任務(wù)也只能等待琅摩,這樣效率并不高铁孵。

那瀏覽器是怎么做的呢?

Event loop: 為了協(xié)調(diào)事件,用戶交互房资,腳本蜕劝,渲染,網(wǎng)絡(luò)等轰异,用戶代理必須使用事件循環(huán)岖沛。
每個(gè)線程都會有它自己的Event loop(事件循環(huán)),所以都能獨(dú)立運(yùn)行搭独。然而所有同源窗口會共享一個(gè)event loop以同步通信婴削。event loop會一直運(yùn)行,來執(zhí)行進(jìn)入隊(duì)列的宏任務(wù)牙肝。一個(gè)event loop有多種的宏任務(wù)源(譯者注:event等等)唉俗,這些宏任務(wù)源保證了在本任務(wù)源內(nèi)的順序。但是瀏覽器每次都會選擇一個(gè)源中的一個(gè)宏任務(wù)去執(zhí)行配椭。這保證了瀏覽器給與一些宏任務(wù)(如用戶輸入)以更高的優(yōu)先級虫溜。好的,跟著我繼續(xù)……

為了更清晰的理解颂郎,需先了解宏任務(wù)吼渡、微任務(wù)的概念。

宏任務(wù)(task)

瀏覽器為了能夠使得JS內(nèi)部task與DOM任務(wù)能夠有序的執(zhí)行乓序,會在一個(gè)task執(zhí)行結(jié)束后寺酪,在下一個(gè) task 執(zhí)行開始前,對頁面進(jìn)行重新渲染 (task->渲染->task->...)替劈。鼠標(biāo)點(diǎn)擊會觸發(fā)一個(gè)事件回調(diào)寄雀,需要執(zhí)行一個(gè)宏任務(wù),然后解析HTMl陨献。還有setTimeout盒犹,它的作用是等待給定的時(shí)間后為它的回調(diào)產(chǎn)生一個(gè)新的宏任務(wù)眨业。這就是為什么打印‘setTimeout’在‘script end’之后。因?yàn)榇蛴 畇cript end’是第一個(gè)宏任務(wù)里面的事情龄捡,而‘setTimeout’是另一個(gè)獨(dú)立的任務(wù)里面打印的。

微任務(wù)(Microtasks )

微任務(wù)通常來說就是需要在當(dāng)前 task 執(zhí)行結(jié)束后立即執(zhí)行的任務(wù)聘殖,比如對一系列動(dòng)作做出反饋行瑞,又或者是需要異步的執(zhí)行任務(wù)而又不需要分配一個(gè)新的 task餐禁,這樣便可以減小一點(diǎn)性能的開銷。只要執(zhí)行棧中沒有其他的js代碼正在執(zhí)行且每個(gè)宏任務(wù)執(zhí)行完帮非,微任務(wù)隊(duì)列會立即執(zhí)行。如果在微任務(wù)執(zhí)行期間微任務(wù)隊(duì)列加入了新的微任務(wù)喜鼓,會將新的微任務(wù)加入隊(duì)列尾部,之后也會被執(zhí)行庄岖。微任務(wù)包括了mutation observe的回調(diào)還有接下來的例子promise的回調(diào)。一旦一個(gè)pormise有了結(jié)果隅忿,或者早已有了結(jié)果(有了結(jié)果是指這個(gè)promise到了fulfilled或rejected狀態(tài)),他就會為它的回調(diào)產(chǎn)生一個(gè)微任務(wù)背桐,這就保證了回調(diào)異步的執(zhí)行即使這個(gè)promise早已有了結(jié)果。所以對一個(gè)已經(jīng)有了結(jié)果的promise調(diào)用.then(yey, nay)會立即產(chǎn)生一個(gè)微任務(wù)链峭。這就是為什么‘promise1’,'promise2'會打印在‘script end’之后畦娄,因?yàn)樗形⑷蝿?wù)執(zhí)行的時(shí)候,當(dāng)前執(zhí)行棧的代碼必須已經(jīng)執(zhí)行完畢弊仪∥蹩ǎ‘promise1’,'promise2'會打印在‘setTimeout’之前是因?yàn)樗形⑷蝿?wù)總會在下一個(gè)宏任務(wù)之前全部執(zhí)行完畢。

image

上圖直觀的展示了宏任務(wù)與微任務(wù)的先后執(zhí)行情況励饵。

消化了這些后驳癌,對于script start, script end, promise1, promise2, setTimeout這樣一個(gè)執(zhí)行結(jié)果是否也就清晰明了了呢?

難度晉級

明白了宏任務(wù)與微任務(wù)后挑戰(zhàn)一下吧役听。
請看下面代碼:

<div class="outer">
 <div class="inner"></div>
</div>
// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');

// Let's listen for attribute changes on the
// outer element
//監(jiān)聽element屬性變化
new MutationObserver(function() {
  console.log('mutate');
}).observe(outer, {
  attributes: true
});

// Here's a click listener…
function onClick() {
  console.log('click');

  setTimeout(function() {
    console.log('timeout');
  }, 0);

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

  outer.setAttribute('data-random', Math.random());
}

// …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);

這時(shí)如果我點(diǎn)擊div.inner會打印什么颓鲜?
有興趣的同學(xué)可以自己嘗試一下,結(jié)果是:
click
promise
mutate
click
promise
mutate
timeout
timeout典予。
demo出處:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/?utm_source=html5weekly

如果在前面例子的js末端添加一句

inner.click();

又會發(fā)生什么呢甜滨?
有興趣的同學(xué)可以自己實(shí)驗(yàn)一下。也可以查看原文獲取更加詳細(xì)的解釋

關(guān)于堆棧與微任務(wù)瘤袖、宏任務(wù)的關(guān)系有興趣的同學(xué)可以點(diǎn)擊下面鏈接了解一下艳吠。
https://juejin.im/post/5b1deac06fb9a01e643e2a95

解答:ajax異步請求是否真的異步?

很多同學(xué)朋友搞不清楚,既然說JavaScript是單線程運(yùn)行的,那么XMLHttpRequest在連接后是否真的異步?
其實(shí)請求確實(shí)是異步的,不過這請求是由瀏覽器新開一個(gè)線程請求(參見上圖),當(dāng)請求的狀態(tài)變更時(shí),如果先前已設(shè)置回調(diào),這異步線程就產(chǎn)生狀態(tài)變更事件放到JavaScript引擎的處理隊(duì)列中等待處理,當(dāng)任務(wù)被處理時(shí),JavaScript引擎始終是單線程運(yùn)行回調(diào)函數(shù),具體點(diǎn)即還是單線程運(yùn)行onreadystatechange所設(shè)置的函數(shù).

解答前文中setTimeout( Function, 0)作用

http://www.cnblogs.com/winner/archive/2008/11/15/1334077.html

參考:http://www.cnblogs.com/hksac/p/6596105.html
https://www.cnblogs.com/woodyblog/p/6061671.html
http://www.cnblogs.com/winner/archive/2008/11/15/1334077.html
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/?utm_source=html5weekly

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末孽椰,一起剝皮案震驚了整個(gè)濱河市昭娩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌黍匾,老刑警劉巖栏渺,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锐涯,居然都是意外死亡磕诊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進(jìn)店門纹腌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霎终,“玉大人,你說我怎么就攤上這事升薯±嘲” “怎么了?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵涎劈,是天一觀的道長广凸。 經(jīng)常有香客問我谅海,道長扭吁,這世上最難降的妖魔是什么盲镶? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任系馆,我火速辦了婚禮由蘑,結(jié)果婚禮上代兵,老公的妹妹穿的比我還像新娘植影。我一直安慰自己,他們只是感情好鹿响,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布惶我。 她就那樣靜靜地躺著,像睡著了一般盯蝴。 火紅的嫁衣襯著肌膚如雪捧挺。 梳的紋絲不亂的頭發(fā)上尿瞭,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天筷厘,我揣著相機(jī)與錄音酥艳,去河邊找鬼。 笑死莫换,一個(gè)胖子當(dāng)著我的面吹牛骤铃,可吹牛的內(nèi)容都是我干的惰爬。 我是一名探鬼主播撕瞧,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼丛版,長吁一口氣:“原來是場噩夢啊……” “哼页畦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起独令,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤逸月,失蹤者是張志新(化名)和其女友劉穎遍膜,沒想到半個(gè)月后瓢颅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挽懦,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡信柿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年渔嚷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了形病。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漠吻。...
    茶點(diǎn)故事閱讀 40,928評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡途乃,死狀恐怖耍共,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情划提,我是刑警寧澤邢享,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站伊履,受9級特大地震影響唐瀑,放射性物質(zhì)發(fā)生泄漏哄辣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一毅弧、第九天 我趴在偏房一處隱蔽的房頂上張望够坐。 院中可真熱鬧元咙,春花似錦庶香、人聲如沸脉课。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽袖瞻。三九已至拆吆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霉晕,已是汗流浹背牺堰。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工恨搓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人斧抱。 一個(gè)月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親眉睹。 傳聞我的和親對象是個(gè)殘疾皇子竹海,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評論 2 361

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