【入門Node.js】之HTTP和EVENT

1、HTTP相關知識

HTTP是一種協議,計算機要共同聽從這種規則,才能相互通訊。html

  • http客戶端發起請求,建立端口
  • http服務器在端口監聽客戶請求
  • http服務器向客戶端返回狀態和內容

瀏覽器打開一個網站,http走過的環節node

 

Ⅰ 首先,對網站進行DNS域名解析

  • 一、瀏覽器搜索自身的DNS緩存
  • 二、搜索操做系統自身的DNS緩存(瀏覽器沒有找到緩存或過時)
  • 三、讀取本地的HOST文件
  • 四、瀏覽器發起一個DNS的一個系統調用

           4.1寬帶運營商服務器查看自己緩存-》npm

4.2運營商服務器發起一個迭代DNS解析的請求(根域->頂級域->域名註冊商(IP地址)) 編程

4.3運營商服務器把結果返回給操做系統內核同時緩存起來 後端

4.4操做系統內核把結果返回給瀏覽器 數組

  • 五、讓瀏覽器拿到網站的IP地址,DNS解析完成,發起HTTP"三次握手"
  • 六、TCP/IP鏈接創建起來後,瀏覽器就能夠向服務器發送HTTP請求,比方說,用HTTP的GET方法請求一個根域裏的一個域名,協議能夠採用HTTP1.0的一個協議
  • 七、服務器端接受到了這個請求,根據路徑參數,通過後端的一些處理以後,把處理後的一個結果的數據返回給瀏覽器。(這時會把網站完整的HTML頁面代碼返回給瀏覽器)
  • 八、瀏覽器拿到了網站的完整的HTML頁面代碼,在解析和渲染這個頁面的時候,裏面的JS、CSS、圖片靜態資源,他們一樣也是一個個HTTP請求,都須要通過上面的主要的七個步驟.
  • 九、瀏覽器根據拿到的資源對頁面進行渲染,最終把一個完整的頁面呈現給了用戶

 

Ⅱ HTTP流程可分爲請求/響應兩部分

HTTP的組成:瀏覽器

  • HTTP: 發送的是一些附加的信息(內容類型服務器發送響應的日期,HTTP狀態碼)
  • 正文信息: 用戶提交的表單信息 

 

2、以具體網站分析HTTP的流程

 

http請求方法: 緩存

  • Get:獲取,讀取數據
  • Post:提交資源
  • Put:更新
  • Delete:刪除
  • Head 與get方法相同,但服務器不傳回資源

 

狀態碼:服務器端返回瀏覽器,告知瀏覽器請求成功或失敗的信息 服務器

  • 1XX請求已經接受
  • 2XX請求成功並處理成功
  • 3XX重定向
  • 4XX客戶端錯誤
  • 5XX服務器端錯誤
  • 200:OK,請求成功
  • 400:客戶端請求有語法錯誤
  • 401:請求未經受權
  • 403:收到請求,但不提供服務
  • 404:資源未找到
  • 500:服務器端未知錯誤
  • 503:服務器端當前不能處理請求

 

 3、事件回調進階

Nodejs中http模塊不解析請求的具體內容,只分離出請求頭和請求體

一、什麼是回調函數?

回調試異步編程時的基礎,將後續邏輯封裝成起始函數的參數,逐層嵌套多線程

function learn(something){
    console.log(something)
}

function we(callback, something){
    something += 'is cool'
    callback(something)
}
//傳入具名函數
we(learn, 'Nodejs')
//傳入匿名函數
we(function(something){
    console.log(something)
}, 'Jade')

二、什麼事同步/異步?

同步:發送方發送數據後,等待接收方發回響應之後才發下一個數據包的通信方式

異步:發送方發出數據後,不等接收方發回響應,接着發送下個數據包的通信方式

三、什麼是I/O?

磁盤的寫入(in)磁盤的讀取(out)

四、什麼是單線程/多線程?

一次只能執行一個程序叫作單線程 一次能執行多個程序叫作多線程

五、什麼是阻塞/非阻塞?

阻塞:前一個程序未執行完就得一直等待

非阻塞:前一個程序未執行完時能夠掛起,繼續執行其餘程序,等到使用時再執行

六、什麼是事件?

一個觸發動做(例如點擊按鈕)

七、什麼是事件驅動?

一個觸發動做引發的操做(例如點擊按鈕後彈出一個對話框)

 

function clickIt(e){
    window.alert('Button is clicked')
}

var button = document.getElementById('#button')
      button.addEventListener('click', clickIt)

八、什麼是基於事件驅動的回調?

爲了某個事件註冊了回調函數,可是這個回調函數不是立刻執行,只有當事件發生的時候,纔會調用回掉函數,這種函數執行的方式叫作事件驅動~這種註冊回調就是基於事件驅動的回調,若是這些回調和異步I/O(數據寫入、讀取)操做相關,能夠看做是基於回調的異步I/O。只不過這種回調在nodejs中是有事件來驅動的

九、什麼是事件循環?

事件循環Eventloop,假若有大量的異步操做,一些I/O的耗時操做,甚至是一些定時器控制的延時操做,它們完成的時候都要調用相應的回調函數,而從完成一些密集的任務,而又不會阻塞整個程序執行的流程,此時須要一種機制來管理,這種機制叫作事件循環 總而言之,管理大量異步操做的機制叫作事件循環

EventLoop: 回調函數隊列,異步執行的函數會被壓入這個隊列;隊列被循環查詢。

 

4、HTTP源碼解讀之做用域與上下文

一、什麼是做用域?

與調用函數,訪問變量的能力有關。

做用域分爲:局部和全局(在局部做用域裏能夠訪問到全局做用域的變量,但在局部做用域外面就訪問不到局部做用域裏面所設定的變量)

//定義一個全局變量
var globalVariable = 'This is global variable'
//定義一個全局做用域
function globalFunction(){
    //在全局做用域裏定義一個局部變量
    var localVariable = 'This is local variable'
          //打印出全局變量和局部變量
          console.log('Visit global/loacal variable')
          console.log(globalVariable)
          console.log(localVariable)

          //改變全局變量
          globalVariable = 'This is changed variable'

          //打印已經改變的全局變量
          console.log(globalVariable)

          //在全局做用域裏定義一個局部函數
          function localFunction(){
                  //在局部做用域裏定義一個局部變量
                  var innerLocalVariable = 'This is inner local variable'
                
                //打印全局變量,局部變量,內部局部變量
                console.log('Visit global/local/innerLocal variable')
                console.log(globalVariable)
                console.log(localVariable)
                console.log(innerLocalVariable)
          }

        localFunction()
}

globalFunction()
//輸出結果
//Visit global/loacal variable
//This is global variable
//This is local variable

//This is changed variable

//Visit global/local/innerLocal variable
//This is changed variable
//This is local variable
//This is inner local variable

二、什麼是上下文?

與this關鍵字有關,是調用當前可執行的代碼的引用,this老是指向調用這個方法的對象

js裏的this一般是當前函數的擁有者 this是js的一個關鍵字,表明函數運行時自動生成的一個內部對象,只能在函數內部使用

1.做爲對象的方法

this在方法的內部,this就指向調用這個方法的對象

var pet ={
    words: '...',
    speak: function(){
        console.log(this.words)// '...'
        console.log(this == pet)//true
    }
}
//speak()方法裏的this指向擁有speak()方法的對象pet,
//因此 this == pet 爲true
pet.speak()

2.函數的調用
this指向執行環境中的全局變量(瀏覽器->window || nodejs ->global)

function pet(words){
    this.words = words;

    console.log(this.words) // '...'
    console.log(this === gloabal)//true
}
pet('...')

3.構造函數
this所在的方法被實例對象所調用,那麼this就指向這個實例化對象

function Pet(words){
    this.words = words
    this.speak = function(){
        console.log(this.words) //Miao
        console.log(this)//打印出this指向的整個實例化對象,{words: 'Miao',speak: [Function]}
    }
}
var cat  = new Pet('Miao')
cat.speak() //speak()裏面的this指向cat
三、更改上下文方法
(更改this指向的內容,可方便地實現繼承):
  • call(list);
  • apply(array);

二者的做用徹底相同,只是接收參數的方式不太同樣

都是爲了改變某個函數運行時的context即上下文而存在的,換句話說,就是爲了改變函數內部this的指向。

var Pet = {
words: '...0',
speak: function(say){
console.log(say+ ' ' +this.words)
}
}
pet.speak('Speak') //Speak ...

var dog = {
words: 'Wang'
}
pet.speak.call(dog, 'Speak') // Speak Wang
//call()將pet.speak指定的原來的對象pet轉換爲指向對象dog

 call()和apply()實現繼承

function Pet(words){
    this.words = words
           this.speak = function(){
                console.log(this.words)          
           }
}
function Dog(words){
    Pet.call(this,words) //直接把this轉化爲指向Dog對象,實現了Dog繼承了Pet
}
var dog = new Dog('Wang')
dog.speak() //Wang

 

5、HTTP小爬蟲

慕課網Nodejs基礎入門課程標題爬蟲

var http = require('http')   
var cheerio = require('cheerio') //將cheerio模塊require進來
/*添加cheerio模塊,就像jQuery同樣操做裝載後的html,
   先安裝該模塊,在cmd命令行中輸入:npm install cheerio -g */
var url = 'http://www.imooc.com/learn/348'   //找到課程頁面,只爬單頁面


function filterChapters(html){
       var $ = cheerio.load(html)  //經過cheerio.load()將html內容裝載進來
       var chapters = $('.chapter')   //先拿到每一大章
       // 想要獲得每一章的內容是一個數組
       //[{
       //     chapterTitle: ' ',
       //     videos: [
       //            title: ' ',
       //            id: ' '
       //     ]
       // }]
       var courseData = [ ]

       chapters.each(function(item){
           var chapter = $(this)
             var chapterTitle = chapter.find('strong').text()
             var videos = chapter.find('.video').children('li')
           //每一章就是一個對象自變量
             var chapterData = {
                 chapterTitle : chapterTitle,
                 videos: [ ]  //該videos如今仍是一個空數組
             }
             //對每個videos進行遍歷,拿到單個video
             videos.each(function(item){
                         var video = $(this).find('.J-media-item')
                         var videoTitle = video.text()  
                         var id = video.attr('href').split('/video')[1]//split()方法是將指定字符串按某指定的分隔符進行拆分,拆將會造成一個字符串的數組並返回

                         chapterData.videos.push({  //將videoTitle和idpush進videos[ ]中
                                  title: videoTitle,
                                  id : id
                         })
             })
             courseData.push(chapterData) //將chapterData數組push進courseData中

       })
       return courseData    //返回回去courseData給filterChapters
}

function printCourseInfo(courseData){
             courseData.forEach(function(item){//由於courseData是一個數組,因此須要遍歷該數組後打開
                         
                         var chapterTitle = item.chapterTitle
                         console.log(chapterTitle+ '\n') //將chapterTitle打印出來

                         item.videos.forEach(function(video){  
                                 console.log(' 【' +video.id +' 】' + video.title +'\n') //遍歷videos後將其打印出來
                         })
             })

             
}
http.get(url, function(res){ //第一個參數傳入url,第二個參數是回調的方法
      var html = ' '   

       res.on('data', function(data){   //response由data觸發的時候,回調data,把html的內容被拼加進來
           html += data
       })

       res.on('end', function(){ //數據累加,最後輸出
           var courseData = filterChapters(html)   //將html做爲參數傳遞給filterChapters,讓該函數去作數據的過濾
           printCourseInfo(courseData)  //經過printCourseInfo函數打印
       })
}).on('error', function(){    //拋出異常,當出現錯誤的時候
    console.log('獲取課程數據出錯!')
})

最後,經過cmd命令行:node crawlers.js將其打印出來

 

其中,在安裝cheerio模塊時出現了錯誤,緣由是沒有全局變量安裝,使用 nmp install cheerio-g

或,選擇配置環境變量解決!

 


 

1、Events

1.EventEmitter支持多個事件監聽,最大爲10,也能夠自定義最大數添加監聽

var EventEmitter = require('events').EventEmitter;
var instance = new EventEmitter();
instance.on('eventName',function(){});

2.也能夠修改最大的監聽事件數,可是會形成內存泄漏

//自定義最大數

每一個setMaxListeners針對的是一個特定事件,設置最大的num
instance.setMaxListeners(num)

3.事件監聽以後,須要emit(發射,發出)纔會執行

instance.emit('eventName',arguments)

4.判斷事件是否監聽

boolean.instance.emit('eventName',arguments) //true or false

5.移除監聽事件

  • 5.1移除單個監聽事件

instance.moveListener('eventName',funcName)//移除事件須要具名函數,匿名函數不可移除

  • 5.2移除多個監聽事件

instance.removeAllListener()//表示移除全部時間的監聽

instance.moveAllListener('eventName')//移除特定的event的全部事件

6.計算事件監聽數量

  1. instance.listeners('eventName').length
  2. EventEmitter.listenerCount('eventName')
var EventEmitter = require('events').EventEmitter //注意是events!

var life = new EventEmitter() //生成實例
life.setMaxListeners(11)

function water(which){
      console.log('The'+which+'is showing...')
}
life.on('date', water)
//爲該實例添加監聽

life.on('date', function(which){
           console.log('The'+which+'is down...')
})
life.on('date', function(which){
           console.log('The'+which+'is funny...')
})
life.on('date', function(which){
           console.log('The'+which+'is cool...')
})
life.on('date', function(which){
           console.log('The'+which+'is dark...')
})
life.on('date', function(which){
           console.log('The'+which+'is light...')
})
life.on('date', function(which){
           console.log('The'+which+'is wonderful...')
})
life.on('date', function(which){
           console.log('The'+which+'is interesting...')
})
life.on('date', function(which){
           console.log('The'+which+'is sad...')
})
life.on('date', function(which){
           console.log('The'+which+'is happy...')
})
life.on('date', function(which){
           console.log('The'+which+'is boring...')
})

/*//判斷事件是否有被監聽過?
var hasFirstListener = life.emit('date','movies')
var hasSecondListener = life.emit('date1','dinner')
var hasThirdListener = life.emit('date2','shopping')

console.log(hasFirstListener)
console.log(hasSecondListener)
console.log(hasThirdListener)*/

//事件超過十個,出現警告。可設置setMaxListener

life.on('date1',function(which){
    console.log('The'+which+'dilicious')
})
life.on('date1',function(which){
    console.log('The'+which+'bad')
})

//移除監聽事件時,只能移除具名函數
life.removeListener('date',water)
//移除全部的監聽事件
life.removeAllListeners('date1')//須要傳入具體事件名稱纔不會所有刪除致使出錯
var hasFirstListener = life.emit('date','movies')
var hasSecondListener = life.emit('date1','dinner')

//查看監聽事件的個數
console.log(life.listeners('date').length) //移除掉一個後只剩10個
console.log(EventEmitter.listenerCount(life,'date'))
相關文章
相關標籤/搜索