nodejs-第二章-第二節-nodejs事件循環(2-2)

process.nextTick
  • process.nextTick()不在event loop的任何階段執行,而是在各個階段切換的中間執行;即從一個階段切換到下個階段前執行;
  • 三種定義異步事件的方式: setTimeout,setImmediate,process.nextTick()
var fs = require('fs');
fs.readFile(__dirname,() =>{
    setTimeout(() =>{
        console.log('setTimeout')
    })
    
    setImmediate(() =>{
        console.log('setImmediate')
        process.nextTick(() =>{
            console.log('nextTick3')
        })
    })
    
    process.nextTick(() =>{
            console.log('nextTick1')
    })
    
    process.nextTick(() =>{
            console.log('nextTick2')
    })
})
// nextTick1 nextTick2 setImmediate nextTick3 setTimeout
nextTick應用場景
  1. 在多個事件交叉執行cpu運算密集型的任務
var http = require('http');
function compute(){
    process.nextTick(compute)
}
http.createServer(function(req,res){
// 服務請求的時候,還能抽空進行一些計算任務;
    res.writeHead(200, {'Content-type': 'text/plain'})
    res.end('hello world');
})
compute()
  • 在這種模式下,咱們不須要遞歸的調用compute(),咱們只須要在事件循環中使用process.nextTict()定義;compute()在下一個事件點執行便可;在這個過程當中,若是有新的http請求進來,事件循環機制會先處理新的請求,而後再調用copute().反之,若是把compute()放在一個遞歸裏調用,那系統一直會阻塞在compute()裏,沒法處理新的http請求了。
  1. 保持回調函數異步執行的原則
  • 當給一個函數定義一個回調函數時,要確保這個回調是異步執行的(定義一個callback,可是又須要在callback裏面使用這個變量);
  • 下面示例違反了這一原則:
function asyncFake(data,callback){ // 同步執行
    if(data === 'foo') callback(true)
    else callback(false)
}
asyncFake('bar',function(result){
    // this callback is actually called synchronously!
})

爲何這樣很差呢?看下面nodejs文檔裏的一段代碼node

var client = net.connect(8124, function(){
    console.log('client connect');
    client.write('world'); // 會報錯
})

在上面的代碼裏,若是由於某種緣由,net.connect()變成同步執行的了,回調函數就會馬上被執行,所以回調函數寫到客戶端的變量就用於不會被初始化了; 這種狀況下咱們就能夠用process.nextTick()把上面的asyncFake改爲異步執行的;app

function asyncReal(data, callback){
    process.nextTick(function(){
        callback(data === 'foo')
    })
}
  1. 用在事件觸發過程當中

EventEmitter 有兩個比較核心的方法,on和Emit。node自帶的發佈/訂閱模式;異步

var EventEmitter = require('events').EventEmmiter;
function StreamLibrary(resourceName){
    this.emit('start')
}
StreamLibrary.prototype.__proto__ = EventEmitter.prototype; // inherit from EventEmitter
var stream = new StreamLibrary('fooResouce');

stream.on('start', function(){
    console.log('Reading has started')
})

以上代碼在new StreamLibrary的時候,已經觸發了emit,此時 尚未訂閱,console.log不會執行 解決方案以下:用異步方法包裝async

function StreamLibrary(resource){
    var self = this;
    // 保證訂閱在發佈以前
    process.nextTick(function(){ 
        self.emit('start');
    })
// read from the file,and for every chunck read.do;
this.emit('data', chunkRead)
}

發佈訂閱模式函數

const EventEmitter = require('events').EventEmitter;
class App extends EventEmmiter{
    
}
let app = new App();

app.on('start',() =>{ // 訂閱
    console.log('start');
})

app.emit('start') // emit 觸發,emit是個同步的方法
console.log(111);  // 若是須要emit是異步的,能夠經過三種異步方法去包裝
// start 111
相關文章
相關標籤/搜索