Node.js事件驅(qū)動

本文翻譯自:Understanding Node.js Event-Driven Architecture

event-drive.jpeg

許多Node.js模塊(諸如Http requests皮服、responses、streams等)內(nèi)置了EventEmitter模塊降淮,因此這些模塊可以通過emit和listen實(shí)現(xiàn)事件的觸發(fā)和監(jiān)聽丙笋。

event-emmiter.png

事件驅(qū)動的本質(zhì)是:以類似回掉函數(shù)的方式,實(shí)現(xiàn)流行的Node.js函數(shù)的調(diào)用(諸如 fs.readFile)。按照這種說法奴潘,當(dāng)Node.js的"callback函數(shù)"準(zhǔn)備就緒后,事件一旦被處罰影钉,"回調(diào)函數(shù)"將作為事件的處理程序画髓。

讓我們一起探索最基本的實(shí)現(xiàn)形式。

Node當(dāng)你準(zhǔn)備好了平委,調(diào)用我

Node處理異步最原始的方法機(jī)會是回調(diào)函數(shù)奈虾,那是在很久以前,Node還沒有內(nèi)置promises和async/await的特性。

回調(diào)函數(shù)作為就是傳遞給其它函數(shù)的參數(shù)肉微。這對Javascript是可行的匾鸥,因?yàn)楹瘮?shù)是第一類對象。

回調(diào)函數(shù)并不意味著異步調(diào)用浪册,這對于理解回調(diào)函數(shù)是至關(guān)重要的扫腺。在方法體中,調(diào)用回調(diào)函數(shù)既可以是同步也可以是異步調(diào)用村象。

例如下面的函數(shù)fileSize笆环,它接收回調(diào)函數(shù)作為參數(shù)并且根據(jù)不同的條件以同步或是異步的方式調(diào)用該回調(diào)函數(shù)。

const readFileAsArray = function(file, cb) {
  fs.readFile(file, function(err, data) {
    if (err) {
      return cb(err);
    }
    const lines = data.toString().trim().split('\n');
    cb(null, lines);
  });
};

readFileAsArray有兩個參數(shù):文件路徑和回調(diào)函數(shù)厚者。readFileArray讀取文件的內(nèi)容躁劣,并把行內(nèi)容切開成數(shù)組,最后把得到的數(shù)組傳遞給回調(diào)函數(shù)中库菲。

下面將是我們應(yīng)用回調(diào)函數(shù)的例子账忘。假設(shè)在相同的路徑下存在一個numbers.txt文件,文件的內(nèi)容如下:

10
11
12
13
14
15

如果我們要計算這個文件有多少奇數(shù)熙宇,我們可以使用下面readFileAsArray函數(shù)中的代碼:

readFileAsArray('./numbers.txt', (err, lines) => {
  if (err) throw err;
  const numbers = lines.map(Number);
  const oddNumbers = numbers.filter(n => n%2 === 1);
  console.log('Odd numbers count:', oddNumbers.length);
});

上面的代碼實(shí)現(xiàn)了:讀取文件中的內(nèi)容鳖擒,把內(nèi)容轉(zhuǎn)換為數(shù)組,計算數(shù)組中的奇數(shù)烫止。

這段代碼是典型的Nodejs回調(diào)函數(shù)蒋荚。在回調(diào)函數(shù)中遵循錯誤優(yōu)先的原則,錯誤信息的參數(shù)可以為空馆蠕,回調(diào)函數(shù)的結(jié)果作為回調(diào)函數(shù)第二個參數(shù)期升。開發(fā)者都應(yīng)該遵循這條原則,因?yàn)榧俣ㄆ渌拇a都是按照這種原則互躬。

現(xiàn)代Javascript對回調(diào)函數(shù)的改進(jìn)

在現(xiàn)代的Javascript中播赁,我們有了promise對象。Promise可以看作是對回調(diào)函數(shù)作異步調(diào)用的優(yōu)化接口吼渡。不再通過將回調(diào)函數(shù)的結(jié)果和回調(diào)函數(shù)可能出現(xiàn)的錯誤作為回調(diào)函數(shù)的參數(shù)容为。Promise對象允許代碼分別處理函數(shù)成功返回結(jié)果和函數(shù)出現(xiàn)的錯誤,通過鏈條的方式處理多個異步回調(diào)函數(shù)寺酪,避免出現(xiàn)回調(diào)嵌套的地獄舟奠。

如果readFileAsArray支持promise,我們就可以寫出下面的代碼:

readFileAsArray('./numbers.txt')
  .then(lines => {
    const numbers = lines.map(Number);
    const oddNumbers = numbers.filter(n => n%2 === 1);
    console.log('Odd numbers count:', oddNumbers.length);
  })
  .catch(console.error);

通過調(diào)用.then函數(shù)獲取異步函數(shù)的結(jié)果房维,而不是將結(jié)果放進(jìn)回調(diào)函數(shù)中沼瘫。 .catch函數(shù)可以獲取回調(diào)函數(shù)的異常信息。

由于新Promise對象的出現(xiàn)咙俩,讓現(xiàn)代的Javascript代碼很方便的支持promise接口耿戚。

下面的代碼就是對回調(diào)函數(shù)進(jìn)行異步調(diào)用的另一種封裝湿故,通過promise對象實(shí)現(xiàn)readFileAsArray函數(shù)的一部調(diào)用:

const readFileAsArray = function(file, cb = () => {}) {
  return new Promise((resolve, reject) => {
    fs.readFile(file, function(err, data) {
      if (err) {
        reject(err);
        return cb(err);
      }
      const lines = data.toString().trim().split('\n');
      resolve(lines);
      cb(null, lines);
    });
  });
};

在fs.readFile函數(shù)外面包裹這Promise對象。通過Promise對象暴露出的兩個參數(shù)(一個resolve函數(shù)膜蛔,另一個reject函數(shù))實(shí)現(xiàn)函數(shù)的封裝坛猪。

當(dāng)我們想引用異步函數(shù)中的錯誤信息,可以調(diào)用reject函數(shù)皂股。當(dāng)我們想使用異步函數(shù)返回的數(shù)據(jù)墅茉,可以調(diào)用resolve函數(shù)。

通過async/await 調(diào)用promise對象

通過Promise對象異步回調(diào)的接口呜呐,可以使代碼在需要異步回調(diào)時變的非常簡單就斤,但是隨著回調(diào)的增多,代碼也會顯得很凌亂蘑辑。

Promise對象對異步回調(diào)優(yōu)化了一點(diǎn)洋机,Generator函數(shù)在Promise對象的基礎(chǔ)上又又優(yōu)化了一點(diǎn)。對異步函數(shù)調(diào)用最友好的方式還要數(shù)async洋魂,通過async函數(shù)我們可以以同步編程的方式绷旗,實(shí)現(xiàn)異步調(diào)用。這種編程方式的出現(xiàn)使得異步代碼的可讀性有了質(zhì)的提升副砍。

下面的代碼就是如何使用async/await調(diào)用readFileAsArray函數(shù):

async function countOdd () {
  try {
    const lines = await readFileAsArray('./numbers');
    const numbers = lines.map(Number);
    const oddCount = numbers.filter(n => n%2 === 1).length;
    console.log('Odd numbers count:', oddCount);
  } catch(err) {
    console.error(err);
  }
}
countOdd();

我們先是創(chuàng)建async函數(shù)衔肢,就是在普通函數(shù)前添加async關(guān)鍵字。在async函數(shù)內(nèi)部豁翎,我們調(diào)用readFileAsArray就像它返回行變量一樣角骤。為了實(shí)現(xiàn)這個功能,我們使用關(guān)鍵字await谨垃。之后,我們繼續(xù)執(zhí)行代碼硼控,就好像readFileAsArray調(diào)用的時同步函數(shù)一樣馒索。

這樣對異步回調(diào)的處理跺涤,使的代碼變的很簡單而且易讀。為了獲取代碼中錯誤信息,我們需要在代碼外面包裹一層try/catch节吮。

通過Async/await的新特性,我們不在需要在代碼寫一些特殊的接口(像 .then 和 .catch)爬早。我們只不過是在一些純Javascript代碼的基礎(chǔ)上氯夷,使用一些函數(shù)標(biāo)記。

我們可以對任何封裝promise對象的函數(shù)使用async/await關(guān)鍵字撼短。然而我們不能使用在回調(diào)式的異步函數(shù)上(如setTimeout)再膳。

EventEmitter模塊

EventEmitter以Node.js異步事件驅(qū)動為內(nèi)核,實(shí)現(xiàn)促進(jìn)Node.js中對象間的通信曲横。Node.js許多內(nèi)置模塊都是繼承自EventEmitter喂柒。

EventEmitter的代碼很簡單:事件的觸發(fā)對象觸發(fā)已經(jīng)注冊的監(jiān)聽其不瓶。因此,事件觸發(fā)對象通常有兩個特點(diǎn):

  • 觸發(fā)已經(jīng)注冊的事件
  • 注冊事件或移除注冊事件監(jiān)聽器

對象繼承EventEmitter灾杰,就可以使用EventEmitter蚊丐。

class MyEmitter extends EventEmitter {

}

通過實(shí)例化已經(jīng)繼承EventEmitter的類生成觸發(fā)事件的對象。

const myEmitter = new MyEmitter();

在事件觸發(fā)對象整個生命周期中艳吠,我們通過觸發(fā)事件名麦备,觸發(fā)任何我們想要作用的事件監(jiān)聽器。

myEmitter.emit('something-happened');

觸發(fā)事件監(jiān)聽器是新條件出現(xiàn)昭娩,這些新條件通常是事件觸發(fā)對象內(nèi)部狀態(tài)改變的信號凛篙。

我們通過on函數(shù)注冊事件監(jiān)聽器,每當(dāng)對象觸發(fā)事件監(jiān)聽器的事件名题禀,這些監(jiān)聽事件將會執(zhí)行鞋诗。

事件并不就是異步

讓我們看一個示例代碼:

const EventEmitter = require('events');

class WithLog extends EventEmitter {
  execute(taskFunc) {
    console.log('Before executing');
    this.emit('begin');
    taskFunc();
    this.emit('end');
    console.log('After executing');
  }
}

const withLog = new WithLog();

withLog.on('begin', () => console.log('About to execute'));
withLog.on('end', () => console.log('Done with execute'));

withLog.execute(() => console.log('*** Executing task ***'));

WithLog是事件觸發(fā)對象,在對象內(nèi)部定義一個execute函數(shù)迈嘹。這個函數(shù)接收一個參數(shù)削彬,這個任務(wù)函數(shù)是一個打印函數(shù)。在這個任務(wù)函數(shù)執(zhí)行前后都觸發(fā)事件秀仲。

為了看清楚事件執(zhí)行的先后順序融痛,我們注冊了相應(yīng)名字的事件監(jiān)聽器,最后我們執(zhí)行WithLog對象中execute函數(shù)神僵。

下面是函數(shù)執(zhí)行的結(jié)果:

Before executing
About to execute
*** Executing task ***
Done with execute
After executing

我想讓大家注意到的是這里的輸出內(nèi)容都是同步的雁刷。在這段代碼中沒有任何異步的行為發(fā)生。

  • 輸出第一行是"Before executing"
  • 以begin命名的事件輸出"About execute"
  • 通過參數(shù)傳遞的函數(shù)輸出"Executing task"
  • 以begin命名的事件輸出"Done with execute"
  • 最后輸出"After executing"

就像古老的回調(diào)函數(shù)保礼,不假設(shè)事件是同步還是異步執(zhí)行沛励。

我們可以假設(shè)下面的測試用例(在withLog對象中的execute函數(shù)是setImmediate):

// ...

withLog.execute(() => {
  setImmediate(() => {
    console.log('*** Executing task ***')
  });
});

現(xiàn)在函數(shù)輸出將會是下面:

Before executing
About to execute
Done with execute
After executing
*** Executing task ***

在異步回調(diào)函數(shù)后,執(zhí)行"Done with execute"和"After executing"將不在正確炮障。

我們需要回調(diào)函數(shù)(或promises對象)與事件驅(qū)動的對象相結(jié)合目派,實(shí)現(xiàn)在異步調(diào)用后執(zhí)行事件觸發(fā)。上面的例子就很好的說明這一點(diǎn)胁赢。

事件相對于傳統(tǒng)回調(diào)函數(shù)還有另一個優(yōu)勢企蹭,程序可以通過定義不同的監(jiān)聽對象,實(shí)現(xiàn)多次觸發(fā)相同的函數(shù)智末。如果通過回調(diào)函數(shù)實(shí)現(xiàn)相同的功能谅摄,則需要在函數(shù)中些許多邏輯。事件是實(shí)現(xiàn)在程序核心基礎(chǔ)上系馆,通過外部插件構(gòu)建函數(shù)的好方法送漠。你可以把事件想象成勾子點(diǎn),通過勾子點(diǎn)狀態(tài)的變化定制一些函數(shù)由蘑。

異步的事件

我們將同步的示例轉(zhuǎn)換為異步將會更有利于我們理解螺男,

const fs = require('fs');
const EventEmitter = require('events');

class WithTime extends EventEmitter {
  execute(asyncFunc, ...args) {
    this.emit('begin');
    console.time('execute');
    asyncFunc(...args, (err, data) => {
      if (err) {
        return this.emit('error', err);
      }

      this.emit('data', data);
      console.timeEnd('execute');
      this.emit('end');
    });
  }
}

const withTime = new WithTime();

withTime.on('begin', () => console.log('About to execute'));
withTime.on('end', () => console.log('Done with execute'));

withTime.execute(fs.readFile, __filename);

WithTime類執(zhí)行異步函數(shù)(asyncFunc)棒厘,在異步函數(shù)中通過console.time和console.timeEnd輸出時間,并在異步函數(shù)執(zhí)行的前后觸發(fā)相應(yīng)的事件下隧。如果異步函數(shù)拋出異常奢人,將會觸發(fā)error/data事件。

我們使用fs.readFile函數(shù)作為測試用例中的異步函數(shù)淆院。通過事件監(jiān)聽何乎,我們就可以代替回掉函數(shù)實(shí)現(xiàn)異步調(diào)用。

代碼執(zhí)行后土辩,相應(yīng)事件按順序觸發(fā)并且得到異步函數(shù)的執(zhí)行時間支救。下面是我們得到的運(yùn)行結(jié)果:

About to execute
execute: 4.507ms
Done with execute

請注意上面的例子是我們通過回調(diào)函數(shù)和事件相結(jié)合完成的。如果我們讓回調(diào)函數(shù)支持promise對象拷淘,我們就可以通過async/await實(shí)現(xiàn)相同的功能各墨。

class WithTime extends EventEmitter {
  async execute(asyncFunc, ...args) {
    this.emit('begin');
    try {
      console.time('execute');
      const data = await asyncFunc(...args);
      this.emit('data', data);
      console.timeEnd('execute');
      this.emit('end');
    } catch(err) {
      this.emit('error', err);
    }
  }
}

我不知道你們是怎么認(rèn)為的,在我看來启涯,這種實(shí)現(xiàn)方式要比基于回調(diào)函數(shù)的代碼或是.then/.catch的代碼要更加易讀贬堵。
async/await的功能使我們盡可能接近JavaScript語言本身,我認(rèn)為這是一個巨大的勝利结洼。

事件參數(shù)和錯誤

在上面的例子中黎做,有兩個事件觸發(fā)時還傳了額外的參數(shù)。

異常事件觸發(fā)異常對象

this.emit('error', err);

數(shù)據(jù)事件觸發(fā)數(shù)據(jù)對象

this.emit('data', data);

我們可以在觸發(fā)事件函數(shù)中松忍,事件名后添加盡可能多的參數(shù)蒸殿,所有這些參數(shù)將會傳遞到事件監(jiān)聽器上。

例如下面的數(shù)據(jù)事件:我們注冊的事件監(jiān)聽器鸣峭,將會獲取我們觸發(fā)事件時傳遞進(jìn)去的參數(shù)(事件名除外)宏所。data對象就是異步函數(shù)asyncFunc返回的數(shù)據(jù)。

withTime.on('data', (data) => {
  // do something with data
});

在我們使用回掉函數(shù)實(shí)現(xiàn)異步調(diào)用的例子中摊溶,如果我們不去監(jiān)聽異常事件爬骤,程序?qū)顺觥?/p>

為了說明這一點(diǎn),在原示例的基礎(chǔ)上更扁,添加調(diào)用產(chǎn)生異常的函數(shù)盖腕。

class WithTime extends EventEmitter {
  execute(asyncFunc, ...args) {
    console.time('execute');
    asyncFunc(...args, (err, data) => {
      if (err) {
        return this.emit('error', err); // Not Handled
      }

      console.timeEnd('execute');
    });
  }
}

const withTime = new WithTime();

withTime.execute(fs.readFile, ''); // BAD CALL
withTime.execute(fs.readFile, __filename);

上面例子中赫冬,WithTime類第一次執(zhí)行將會產(chǎn)生異常浓镜。程序?qū)罎⒉⑼顺觥?/p>

events.js:163
      throw er; // Unhandled 'error' event
      ^
Error: ENOENT: no such file or directory, open ''

WithTime類第二次執(zhí)行將因?yàn)槌绦虻谋罎ⅲ艿接绊懢⒀幔瑥亩荒鼙粓?zhí)行膛薛。

如果在代碼中注冊異常監(jiān)聽器,node程序的生命周期將會發(fā)生變化补鼻。如下例:

withTime.on('error', (err) => {
  // do something with err, for example log it somewhere
  console.log(err)
});

如果按照上面的方法哄啄,第一次執(zhí)行execute函數(shù)產(chǎn)生的異常將會被捕獲雅任,node的生命周期也不會終止。這樣就不會影響代碼繼續(xù)向下執(zhí)行咨跌, 在程序控制端將會輸出:

{ Error: ENOENT: no such file or directory, open '' errno: -2, code: 'ENOENT', syscall: 'open', path: '' }
execute: 4.276ms

值得注意沪么,程序現(xiàn)在的行為,與以promise對象為基礎(chǔ)的函數(shù)的行為不相同锌半。僅僅輸出一個警告禽车,但是程序的正常運(yùn)行并不會受到影響。

UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: ENOENT: no such file or directory, open ''
DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

通過觸發(fā)在全局定義的uncaughtException事件監(jiān)聽器刊殉,是另外一種捕獲異常的方式殉摔。然后,使用全局事件監(jiān)聽器捕獲異常是不明智的選擇记焊。

一般建議避免使用全局uncaughtException事件監(jiān)聽器逸月,但是如果你必須要這么做(報告發(fā)生了異常或是清理緩存)遍膜,這時必須要終止程序碗硬。像如下代碼:

process.on('uncaughtException', (err) => {
  // something went unhandled.
  // Do any cleanup and exit anyway!

  console.error(err); // don't do just that.

  // FORCE exit the process too.
  process.exit(1);
});

但是,如果在同一時間發(fā)生多次觸發(fā)異常事件捌归。這就意味著uncaughtException事件監(jiān)聽器將會被多次觸發(fā)肛响,這對于清理緩存(清除內(nèi)存),將會是災(zāi)難惜索。一個例子是對數(shù)據(jù)庫關(guān)閉的操作可能會被多次觸發(fā)特笋。

EventEmitter模塊暴露一個once方法。即使多次通過once觸發(fā)事件監(jiān)聽器巾兆,這個方法只會讓事件監(jiān)聽器觸發(fā)一次猎物。因此,這是一個使用uncaughtException事件監(jiān)聽器很實(shí)用的方法角塑,因?yàn)榈谝淮斡|發(fā)出現(xiàn)異常時蔫磨,我們就會做一些數(shù)據(jù)庫或內(nèi)存的清理,然后退出程序圃伶。

監(jiān)聽事件的次序

如果對同一事件注冊多個監(jiān)聽器堤如,那么這些監(jiān)聽器的調(diào)用將會按順序進(jìn)行。

// ?????
withTime.on('data', (data) => {
  console.log(`Length: ${data.length}`);
});

// ?????
withTime.on('data', (data) => {
  console.log(`Characters: ${data.toString().length}`);
});

withTime.execute(fs.readFile, __filename);

上面代碼的執(zhí)行后窒朋,輸出的結(jié)果是"Length"行在"Characters"行之前搀罢,因?yàn)槲覀兌x的事件監(jiān)聽器將會按順序執(zhí)行。

如果你定義新的事件監(jiān)聽器侥猩,我們要想做這個事件監(jiān)聽器最先被觸發(fā)榔至,可以使用prependListener方法。

// ?????
withTime.on('data', (data) => {
  console.log(`Length: ${data.length}`);
});

// ?????
withTime.prependListener('data', (data) => {
  console.log(`Characters: ${data.toString().length}`);
});

withTime.execute(fs.readFile, __filename);

上面的代碼將會使"Characters"的結(jié)果將會被最先輸出欺劳。

最后唧取,如果需要刪除事件監(jiān)聽器铅鲤,我們可以使用removeListener方法。

這就是我關(guān)于這個主題的所有闡述枫弟,非常感謝您的閱讀邢享,期待下次與你相遇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末淡诗,一起剝皮案震驚了整個濱河市驼仪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌袜漩,老刑警劉巖绪爸,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異宙攻,居然都是意外死亡奠货,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門座掘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來递惋,“玉大人,你說我怎么就攤上這事溢陪∑妓洌” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵形真,是天一觀的道長杉编。 經(jīng)常有香客問我,道長咆霜,這世上最難降的妖魔是什么邓馒? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蛾坯,結(jié)果婚禮上光酣,老公的妹妹穿的比我還像新娘。我一直安慰自己脉课,他們只是感情好救军,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著倘零,像睡著了一般唱遭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上视事,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天胆萧,我揣著相機(jī)與錄音庆揩,去河邊找鬼俐东。 笑死跌穗,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的虏辫。 我是一名探鬼主播蚌吸,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼砌庄!你這毒婦竟也來了羹唠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤娄昆,失蹤者是張志新(化名)和其女友劉穎佩微,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萌焰,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哺眯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了扒俯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奶卓。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖撼玄,靈堂內(nèi)的尸體忽然破棺而出夺姑,到底是詐尸還是另有隱情,我是刑警寧澤掌猛,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布盏浙,位于F島的核電站,受9級特大地震影響荔茬,放射性物質(zhì)發(fā)生泄漏只盹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一兔院、第九天 我趴在偏房一處隱蔽的房頂上張望殖卑。 院中可真熱鬧,春花似錦坊萝、人聲如沸孵稽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽菩鲜。三九已至,卻和暖如春惦积,著一層夾襖步出監(jiān)牢的瞬間接校,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛛勉,地道東北人鹿寻。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像诽凌,于是被迫代替她去往敵國和親毡熏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

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