国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

《Node.js設計模式》歡迎來到Node.js平臺

Paul_King / 1239人閱讀

摘要:事件多路復用器收集資源的事件并且把這些事件放入隊列中,直到事件被處理時都是阻塞狀態。最后,處理事件多路復用器返回的每個事件,此時,與系統資源相關聯的事件將被讀并且在整個操作中都是非阻塞的。

本系列文章為《Node.js Design Patterns Second Edition》的原文翻譯和讀書筆記,在GitHub連載更新,同步翻譯版鏈接。

歡迎關注我的專欄,之后的博文將在專欄同步:

Encounter的掘金專欄

知乎專欄 Encounter的編程思考

segmentfault專欄 前端小站

Welcom to the Node.js Platform Node.js 的發展

技術本身的發展

龐大的Node.js生態圈的發展

官方組織的維護

Node.js的特點 小模塊

package的形式盡可能多的復用模塊,原則上每個模塊的容量盡量小而精。

原則:

"Small is beautiful" ---小而精

"Make each program do one thing well" ---單一職責原則

因此,一個Node.js應用由多個包搭建而成,包管理器(npm)的管理使得他們相互依賴而不起沖突。

如果設計一個Node.js的模塊,盡可能做到以下三點:

易于理解和使用

易于測試和維護

考慮到對客戶端(瀏覽器)的支持更友好

以及,Don"t Repeat Yourself(DRY)復用性原則。

以接口形式提供

每個Node.js模塊都是一個函數(類也是以構造函數的形式呈現),我們只需要調用相關API即可,而不需要知道其它模塊的實現。Node.js模塊是為了使用它們而創建,不僅僅是在拓展性上,更要考慮到維護性和可用性。

簡單且實用

“簡單就是終極的復雜” ————達爾文

遵循KISS(Keep It Simple, Stupid)原則,即優秀的簡潔的設計,能夠更有效地傳遞信息。

設計必須很簡單,無論在實現還是接口上,更重要的是實現比接口更簡單,簡單是重要的設計原則。

我們做一個設計簡單,功能完備,而不是完美的軟件:

實現起來需要更少的努力

允許用更少的速度進行更快的運輸資源

具有伸縮性,更易于維護和理解

促進社區貢獻,允許軟件本身的成長和改進

而對于Node.js而言,因為其支持JavaScript,簡單和函數、閉包、對象等特性,可取代復雜的面向對象的類語法。如單例模式和裝飾者模式,它們在面向對象的語言都需要很復雜的實現,而對于JavaScript則較為簡單。

介紹Node.js 6 和 ES2015的新語法 let和const關鍵字

ES5之前,只有函數和全局作用域。

if (false) {
  var x = "hello";
}

console.log(x); // undefined

現在用let,創建詞法作用域,則會報出一個錯誤Uncaught ReferenceError: x is not defined

if (false) {
  let x = "hello";
}

console.log(x);

在循環語句中使用let,也會報錯Uncaught ReferenceError: i is not defined

for (let i = 0; i < 10; i++) {
  // do something here
}

console.log(i);

使用letconst關鍵字,可以讓代碼更安全,如果意外的訪問另一個作用域的變量,更容易發現錯誤。

使用const關鍵字聲明變量,變量不會被意外更改。

const x = "This will never change";
x = "...";

這里會報出一個錯誤Uncaught TypeError: Assignment to constant variable.

但是對于對象屬性的更改,const顯得毫無辦法:

const x = {};
x.name = "John";

上述代碼并不會報錯

但是如果直接更改對象,還是會拋出一個錯誤。

const x = {};
x = null;

實際運用中,我們使用const引入模塊,防止意外被更改:

const path = require("path");
let path = "./some/path";

上述代碼會報錯,提醒我們意外更改了模塊。

如果需要創建不可變對象,只是簡單的使用const是不夠的,需要使用Object.freeze()或deep-freeze

我看了一下源碼,其實很少,就是遞歸使用Object.freeze()

module.exports = function deepFreeze (o) {
  Object.freeze(o);

  Object.getOwnPropertyNames(o).forEach(function (prop) {
    if (o.hasOwnProperty(prop)
    && o[prop] !== null
    && (typeof o[prop] === "object" || typeof o[prop] === "function")
    && !Object.isFrozen(o[prop])) {
      deepFreeze(o[prop]);
    }
  });
  
  return o;
};
箭頭函數

箭頭函數更易于理解,特別是在我們定義回調的時候:

const numbers = [2, 6, 7, 8, 1];
const even = numbers.filter(function(x) {
  return x % 2 === 0;
});

使用箭頭函數語法,更簡潔:

const numbers = [2, 6, 7, 8, 1];
const even = numbers.filter(x => x % 2 === 0);

如果不止一個return語句則使用=> {}

const numbers = [2, 6, 7, 8, 1];
const even = numbers.filter((x) => {
  if (x % 2 === 0) {
    console.log(x + " is even");
    return true;
  }
});

最重要是,箭頭函數綁定了它的詞法作用域,其this與父級代碼塊的this相同。

function DelayedGreeter(name) {
  this.name = name;
}

DelayedGreeter.prototype.greet = function() {
  setTimeout(function cb() {
    console.log("Hello" + this.name);
  }, 500);
}

const greeter = new DelayedGreeter("World");
greeter.greet(); // "Hello"

要解決這個問題,使用箭頭函數或bind

function DelayedGreeter(name) {
  this.name = name;
}

DelayedGreeter.prototype.greet = function() {
  setTimeout(function cb() {
    console.log("Hello" + this.name);
  }.bind(this), 500);
}

const greeter = new DelayedGreeter("World");
greeter.greet(); // "HelloWorld"

或者箭頭函數,與父級代碼塊作用域相同:

function DelayedGreeter(name) {
  this.name = name;
}

DelayedGreeter.prototype.greet = function() {
  setTimeout(() => console.log("Hello" + this.name), 500);
}

const greeter = new DelayedGreeter("World");
greeter.greet(); // "HelloWorld"
類語法糖

class是原型繼承的語法糖,對于來自傳統的面向對象語言的所有開發人員(如JavaC#)來說更熟悉,新語法并沒有改變JavaScript的運行特征,通過原型來完成更加方便和易讀。

傳統的通過構造器 + 原型的寫法:

function Person(name, surname, age) {
  this.name = name;
  this.surname = surname;
  this.age = age;
}

Person.prototype.getFullName = function() {
  return this.name + "" + this.surname;
}

Person.older = function(person1, person2) {
  return (person1.age >= person2.age) ? person1 : person2;
}

使用class語法顯得更加簡潔、方便、易懂:

class Person {
  constructor(name, surname, age) {
    this.name = name;
    this.surname = surname;
    this.age = age;
  }

  getFullName() {
    return this.name + "" + this.surname;
  }

  static older(person1, person2) {
    return (person1.age >= person2.age) ? person1 : person2;
  }
}

但是上面的實現是可以互換的,但是,對于class語法來說,最有意義的是extendssuper關鍵字。

class PersonWithMiddlename extends Person {
  constructor(name, middlename, surname, age) {
    super(name, surname, age);
    this.middlename = middlename;
  }

  getFullName() {
    return this.name + "" + this.middlename + "" + this.surname;
  }
}

這個例子是真正的面向對象的方式,我們聲明了一個希望被繼承的類,定義新的構造器,并可以使用super關鍵字調用父構造器,并重寫getFullName方法,使得其支持middlename

對象字面量的新語法 允許缺省值:
const x = 22;
const y = 17;
const obj = { x, y };
允許省略方法名
module.exports = {
  square(x) {
    return x * x;
  },
  cube(x) {
    return x * x * x;
  },
};
key的計算屬性
const namespace = "-webkit-";
const style = {
  [namespace + "box-sizing"]: "border-box",
  [namespace + "box-shadow"]: "10px 10px 5px #888",
};
新的定義getter和setter方式
const person = {
  name: "George",
  surname: "Boole",

  get fullname() {
    return this.name + " " + this.surname;
  },

  set fullname(fullname) {
    let parts = fullname.split(" ");
    this.name = parts[0];
    this.surname = parts[1];
  }
};

console.log(person.fullname); // "George Boole"
console.log(person.fullname = "Alan Turing"); // "Alan Turing"
console.log(person.name); // "Alan"

這里,第二個console.log觸發了set方法。

模板字符串 其它ES2015語法

函數默認參數

剩余參數語法

拓展運算符

解構賦值

new.target

代理

反射

Symbol

reactor模式

reactor模式Node.js異步編程的核心模塊,其核心概念是:單線程、非阻塞I/O,通過下列例子可以看到reactor模式Node.js平臺的體現。

I/O是緩慢的

在計算機的基本操作中,輸入輸出肯定是最慢的。訪問內存的速度是納秒級(10e-9 s),同時訪問磁盤上的數據或訪問網絡上的數據則更慢,是毫秒級(10e-3 s)。內存的傳輸速度一般認為是GB/s來計算,然而磁盤或網絡的訪問速度則比較慢,一般是MB/s。雖然對于CPU而言,I/O操作的資源消耗并不算大,但是在發送I/O請求和操作完成之間總會存在時間延遲。除此之外,我們還必須考慮人為因素,通常情況下,應用程序的輸入是人為產生的,例如:按鈕的點擊、即時聊天工具的信息發送。因此,輸入輸出的速度并不因網絡和磁盤訪問速率慢造成的,還有多方面的因素。

阻塞I/O

在一個阻塞I/O模型的進程中,I/O請求會阻塞之后代碼塊的運行。在I/O請求操作完成之前,線程會有一段不定長的時間浪費。(它可能是毫秒級的,但甚至有可能是分鐘級的,如用戶按著一個按鍵不放的情況)。以下例子就是一個阻塞I/O模型。

// 直到請求完成,數據可用,線程都是阻塞的
data = socket.read();
// 請求完成,數據可用
print(data);

我們知道,阻塞I/O的服務器模型并不能在一個線程中處理多個連接,每次I/O都會阻塞其它連接的處理。出于這個原因,對于每個需要處理的并發連接,傳統的web服務器的處理方式是新開一個新的進程或線程(或者從線程池中重用一個進程)。這樣,當一個線程因 I/O操作被阻塞時,它并不會影響另一個線程的可用性,因為他們是在彼此獨立的線程中處理的。

通過下面這張圖:

通過上面的圖片我們可以看到每個線程都有一段時間處于空閑等待狀態,等待從關聯連接接收新數據。如果所有種類的I/O操作都會阻塞后續請求。例如,連接數據庫和訪問文件系統,現在我們能很快知曉一個線程需要因等待I/O操作的結果等待許多時間。不幸的是,一個線程所持有的CPU資源并不廉價,它需要消耗內存、造成CPU上下文切換,因此,長期占有CPU而大部分時間并沒有使用的線程,在資源利用率上考慮,并不是高效的選擇。

非阻塞I/O

阻塞I/O之外,大部分現代的操作系統支持另外一種訪問資源的機制,即非阻塞I/O。在這種機制下,后續代碼塊不會等到I/O請求數據的返回之后再執行。如果當前時刻所有數據都不可用,函數會先返回預先定義的常量值(如undefined),表明當前時刻暫無數據可用。

例如,在Unix操作系統中,fcntl()函數操作一個已存在的文件描述符,改變其操作模式為非阻塞I/O(通過O_NONBLOCK狀態字)。一旦資源是非阻塞模式,如果讀取文件操作沒有可讀取的數據,或者如果寫文件操作被阻塞,讀操作或寫操作返回-1EAGAIN錯誤。

非阻塞I/O最基本的模式是通過輪詢獲取數據,這也叫做忙-等模型。看下面這個例子,通過非阻塞I/O和輪詢機制獲取I/O的結果。

resources = [socketA, socketB, pipeA];
while(!resources.isEmpty()) {
  for (i = 0; i < resources.length; i++) {
    resource = resources[i];
    // 進行讀操作
    let data = resource.read();
    if (data === NO_DATA_AVAILABLE) {
      // 此時還沒有數據
      continue;
    }
    if (data === RESOURCE_CLOSED) {
      // 資源被釋放,從隊列中移除該鏈接
      resources.remove(i);
    } else {
      consumeData(data);
    }
  }
}

我們可以看到,通過這個簡單的技術,已經可以在一個線程中處理不同的資源了,但依然不是高效的。事實上,在前面的例子中,用于迭代資源的循環只會消耗寶貴的CPU,而這些資源的浪費比起阻塞I/O反而更不可接受,輪詢算法通常浪費大量CPU時間。

事件多路復用

對于獲取非阻塞的資源而言,忙-等模型不是一個理想的技術。但是幸運的是,大多數現代的操作系統提供了一個原生的機制來處理并發,非阻塞資源(同步事件多路復用器)是一個有效的方法。這種機制被稱作事件循環機制,這種事件收集和I/O隊列源于發布-訂閱模式。事件多路復用器收集資源的I/O事件并且把這些事件放入隊列中,直到事件被處理時都是阻塞狀態??聪旅孢@個偽代碼:

socketA, pipeB;
wachedList.add(socketA, FOR_READ);
wachedList.add(pipeB, FOR_READ);
while(events = demultiplexer.watch(wachedList)) {
  // 事件循環
  foreach(event in events) {
    // 這里并不會阻塞,并且總會有返回值(不管是不是確切的值)
    data = event.resource.read();
    if (data === RESOURCE_CLOSED) {
      // 資源已經被釋放,從觀察者隊列移除
      demultiplexer.unwatch(event.resource);
    } else {
      // 成功拿到資源,放入緩沖池
      consumeData(data);
    }
  }
}

事件多路復用的三個步驟:

資源被添加到一個數據結構中,為每個資源關聯一個特定的操作,在這個例子中是read

事件通知器由一組被觀察的資源組成,一旦事件即將觸發,會調用同步的watch函數,并返回這個可被處理的事件。

最后,處理事件多路復用器返回的每個事件,此時,與系統資源相關聯的事件將被讀并且在整個操作中都是非阻塞的。直到所有事件都被處理完時,事件多路復用器會再次阻塞,然后重復這個步驟,以上就是event loop。

上圖可以很好的幫助我們理解在一個單線程的應用程序中使用同步的時間多路復用器和非阻塞I/O實現并發。我們能夠看到,只使用一個線程并不會影響我們處理多個I/O任務的性能。同時,我們看到任務是在單個線程中隨著時間的推移而展開的,而不是分散在多個線程中。我們看到,在單線程中傳播的任務相對于多線程中傳播的任務反而節約了線程的總體空閑時間,并且更利于程序員編寫代碼。在這本書中,你可以看到我們可以用更簡單的并發策略,因為不需要考慮多線程的互斥和同步問題。

在下一章中,我們有更多機會討論Node.js的并發模型。

介紹reactor模式

現在來說reactor模式,它通過一種特殊的算法設計的處理程序(在Node.js中是使用一個回調函數表示),一旦事件產生并在事件循環中被處理,那么相關handler將會被調用。

它的結構如圖所示:

reactor模式的步驟為:

應用程序通過提交請求到時間多路復用器產生一個新的I/O操作。應用程序指定handlerhandler 在操作完成后被調用。提交請求到事件多路復用器是非阻塞的,其調用所以會立馬返回,將執行權返回給應用程序。

當一組I/O操作完成,事件多路復用器會將這些新事件添加到事件循環隊列中。

此時,事件循環會迭代事件循環隊列中的每個事件。

對于每個事件,對應的handler被處理。

handler,是應用程序代碼的一部分,handler執行結束后執行權會交回事件循環。但是,在handler 執行時可能請求新的異步操作,從而新的操作被添加到事件多路復用器。

當事件循環隊列的全部事件被處理完后,循環會在事件多路復用器再次阻塞直到有一個新的事件可處理觸發下一次循環。

我們現在可以定義Node.js的核心模式:

模式(反應器)阻塞處理I/O到在一組觀察的資源有新的事件可處理,然后以分派每個事件對應handler的方式反應。

OS的非阻塞I/O引擎

每個操作系統對于事件多路復用器有其自身的接口,Linuxepoll,Mac OSXkqueue,WindowsIOCP API。除外,即使在相同的操作系統中,每個I/O操作對于不同的資源表現不一樣。例如,在Unix下,普通文件系統不支持非阻塞操作,所以,為了模擬非阻塞行為,需要使用在事件循環外用一個獨立的線程。所有這些平臺內和跨平臺的不一致性需要在事件多路復用器的上層做抽象。這就是為什么Node.js為了兼容所有主流平臺而
編寫C語言庫libuv,目的就是為了使得Node.js兼容所有主流平臺和規范化不同類型資源的非阻塞行為。libuv今天作為Node.jsI/O引擎的底層。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89981.html

相關文章

  • SpringCloud(第 026 篇)簡單異構系統之 nodejs 微服務

    摘要:第篇簡單異構系統之微服務一大致介紹因為在后面要利用集成異構系統,所以才有了本章節的微服務本章節使用了最簡單的請求截取的方式,截取不同的后綴做不同的響應處理,簡直二實現步驟添加服務端文件引入模塊創建獲得請求的路徑訪問,將會返回歡迎 SpringCloud(第 026 篇)簡單異構系統之 nodejs 微服務 - 一、大致介紹 1、因為在后面要利用 SpringCloud 集成異構系統,所...

    raledong 評論0 收藏0
  • Node.js 微信公眾號實戰】4.Node.js 微信消息管理

    摘要:消息推送也是微信公眾號開發更為有趣的功能,涉及到文本消息圖片消息語音消息視頻消息音樂消息以及圖文消息。在文件中創建文件用于消息的管理。 一、寫在前面的話 ??當用戶發送消息給公眾號時(或某些特定的用戶操作引發的事件推送時),會產生一個POST請求,開發者可以在響應包(Get)中返回特定XML結構,來對該消息進行響應。 ??消息推送也是微信公眾號開發更為有趣的功能,涉及到文本消息、圖片消...

    lewinlee 評論0 收藏0
  • 那些年的體驗技術部

    摘要:隨著業務的爆發,團隊人數迅速增長起來,團隊名也從前端開發部改名成體驗技術部,意在體現前端工程師的核心競爭力用技術解決產品體驗問題。前后端分離的研發模式在社區流行起來,體驗技術部最先實踐的是基于的應用層方案。2008 年對中國人是復雜的一年,冰災,大地震,奧運會接踵而至。對玉伯來說也一樣,趕在奧運會排查臨時人口之前,玉伯從北京中科院軟件所離開,憑著自己幾年來在程序開發上的經歷和對新興前端行業的...

    sean 評論0 收藏0
  • node技術棧 - 收藏集 - 掘金

    摘要:異步最佳實踐避免回調地獄前端掘金本文涵蓋了處理異步操作的一些工具和技術和異步函數。 Nodejs 連接各種數據庫集合例子 - 后端 - 掘金Cassandra Module: cassandra-driver Installation ... 編寫 Node.js Rest API 的 10 個最佳實踐 - 前端 - 掘金全文共 6953 字,讀完需 8 分鐘,速讀需 2 分鐘。翻譯自...

    王偉廷 評論0 收藏0
  • Node.js 配合 express 框架、mongodb 實踐 && [使用 T

    摘要:一默認使用的模塊化方案,默認是的模塊化方案,兩者有本質區別。的去尋找引入的依賴時,如果是自帶的模塊,比如文件模塊,只需要填寫即可。這是版本入口文件使用了兩個路由器路由,分別處理和請求邏輯。核心操作全部依賴模型對象來執行。 一、Node.js默認使用commonJs的模塊化方案,TypeScript默認是ES6的模塊化方案,兩者有本質區別。 1.Node.js的去尋找引入的依賴時,如果...

    wmui 評論0 收藏0

發表評論

0條評論

Paul_King

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<