国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > JS > 正文

淺談Node異步編程的機制

2024-05-06 16:40:22
字體:
來源:轉載
供稿:網友

本文介紹了Node異步編程,分享給大家,具體如下:

目前的異步編程主要解決方案有:

  • 事件發布/訂閱模式
  • Promise/Deferred模式
  • 流程控制庫

事件發布/訂閱模式

Node自身提供了events模塊,可以輕松實現事件的發布/訂閱

//訂閱emmiter.on("event1",function(message){  console.log(message);})//發布emmiter.emit("event1","I am mesaage!");

偵聽器可以很靈活地添加和刪除,使得事件和具體處理邏輯之間可以很輕松的關聯和解耦

事件發布/訂閱模式常常用來解耦業務邏輯,事件發布者無需關注訂閱的偵聽器如何實現業務邏輯,甚至不用關注有多少個偵聽器存在,數據通過消息的方式可以很靈活的進行傳遞。

下面的HTTP就是典型的應用場景

var req = http.request(options,function(res){  res.on('data',function(chunk){    console.log('Body:'+ chunk);  })  res.on('end',function(){    //TODO  })})

如果一個事件添加了超過10個偵聽器,將會得到一條警告,可以通過調用emmite.setMaxListeners(0)將這個限制去掉

繼承events模塊

var events = require('events');function Stream(){  events.EventEmiiter.call(this);}util.inherits(Stream,events.EventEmitter);

利用事件隊列解決雪崩問題

所謂雪崩問題,就是在高訪問量,大并發量的情況下緩存失效的情況,此時大量的請求同時融入數據庫中,數據庫無法同時承受如此大的查詢請求,進而往前影響到網站整體的響應速度

解決方案:

var proxy = new events.EventEmitter();var status = "ready"; var seletc = function(callback){  proxy.once("selected",callback);//為每次請求訂閱這個查詢時間,推入事件回調函數隊列  if(status === 'ready'){     status = 'pending';//設置狀態為進行中以防止引起多次查詢操作    db.select("SQL",function(results){      proxy.emit("selected",results); //查詢操作完成后發布時間      status = 'ready';//重新定義為已準備狀態    })  }}

多異步之間的協作方案

以上情況事件與偵聽器的關系都是一對多的,但在異步編程中,也會出現事件與偵聽器多對一的情況。

這里以渲染頁面所需要的模板讀取、數據讀取和本地化資源讀取為例簡要介紹一下

var count = 0 ;var results = {};var done = function(key,value){  result[key] = value;  count++;  if(count === 3){    render(results);  }}fs.readFile(template_path,"utf8",function(err,template){  done('template',template)})db.query(sql,function(err,data){  done('data',data);})l10n.get(function(err,resources){  done('resources',resources)})

偏函數方案

var after = function(times,callback){  var count = 0, result = {};  return function(key,value){    results[key] = value;    count++;    if(count === times){      callback(results);    }  }}var done = after(times,render);var emitter = new events.Emitter();emitter.on('done',done);  //一個偵聽器emitter.on('done',other);  //如果業務增長,可以完成多對多的方案fs.readFile(template_path,"utf8",function(err,template){  emitter.emit('done','template',template);})db.query(sql,function(err,data){  emitter.emit('done','data',data);})l10n.get(function(err,resources){  emitter.emit('done','resources',resources)})

引入EventProxy模塊方案

var proxy = new EventProxy();proxy.all('template','data','resources',function(template,data,resources){  //TODO})fs.readFile(template_path,'utf8',function(err,template){  proxy.emit('template',template);})db.query(sql,function(err,data){  proxy.emit('data',data);})l10n.get(function(err,resources){  proxy.emit('resources',resources);})

Promise/Deferred模式

以上使用事件的方式時,執行流程都需要被預先設定,這是發布/訂閱模式的運行機制所決定的。

$.get('/api',{  success:onSuccess,  err:onError,  complete:onComplete})//需要嚴謹設置目標

那么是否有一種先執行異步調用,延遲傳遞處理的方式的?接下來要說的就是針對這種情況的方式:Promise/Deferred模式

Promise/A

Promise/A提議對單個異步操作做出了這樣的抽象定義:

  • Promise操作只會處在三種狀態的一種:未完成態,完成態和失敗態。
  • Promise的狀態只會出現從未完成態向完成態或失敗態轉化,不能逆反,完成態和失敗態不能相互轉化
  • Promise的狀態一旦轉化,就不能被更改。

一個Promise對象只要具備then()即可

  • 接受完成態、錯誤態的回調方法
  • 可選地支持progress事件回調作為第三個方法
  • then()方法只接受function對象,其余對象將被忽略
  • then()方法繼續返回Promise對象,以實現鏈式調用

通過Node的events模塊來模擬一個Promise的實現

var Promise = function(){  EventEmitter.call(this)}util.inherits(Promise,EventEmitter);Promise.prototype.then = function(fulfilledHandler,errHandler,progeressHandler){  if(typeof fulfilledHandler === 'function'){    this.once('success',fulfilledHandler); //實現監聽對應事件  }  if(typeof errorHandler === 'function'){    this.once('error',errorHandler)  }  if(typeof progressHandler === 'function'){    this.on('progress',progressHandler);  }  return this;}

以上通過then()將回調函數存放起來,接下來就是等待success、error、progress事件被觸發,實現這個功能的對象稱為Deferred對象,即延遲對象。

var Deferred = function(){  this.state = 'unfulfilled';  this.promise = new Promise();}Deferred.prototype.resolve = function(obj){ //當異步完成后可將resolve作為回調函數,觸發相關事件  this.state = 'fulfilled';  this.promise.emit('success',obj);}Deferred.prototype.reject = function(err){  this.state = 'failed';  this.promise.emit('error',err);}Deferred.prototype.progress = function(data){  this.promise.emit('progress',data)}

因此,可以對一個典型的響應對象進行封裝

res.setEncoding('utf8');res.on('data',function(chunk){  console.log("Body:" + chunk);})res.on('end',function(){  //done})res.on('error',function(err){  //error}

轉換成

res.then(function(){  //done},function(err){  //error},function(chunk){  console.log('Body:' + chunk);})

要完成上面的轉換,首先需要對res對象進行封裝,對data,end,error等事件進行promisify

var promisify = function(res){  var deferred = new Deferred(); //創建一個延遲對象來在res的異步完成回調中發布相關事件  var result = ''; //用來在progress中持續接收數據  res.on('data',function(chunk){ //res的異步操作,回調中發布事件    result += chunk;    deferred.progress(chunk);  })  res.on('end',function(){        deferred.resolve(result);  })  res.on('error',function(err){    deferred.reject(err);  });  return deferred.promise   //返回deferred.promise,讓外界不能改變deferred的狀態,只能讓promise的then()方法去接收外界來偵聽相關事件。}promisify(res).then(function(){  //done},function(err){  //error},function(chunk){  console.log('Body:' + chunk);})

以上,它將業務中不可變的部分封裝在了Deferred中,將可變的部分交給了Promise

Promise中的多異步協作

Deferred.prototype.all = function(promises){  var count = promises.length; //記錄傳進的promise的個數  var that = this; //保存調用all的對象  var results = [];//存放所有promise完成的結果  promises.forEach(function(promise,i){//對promises逐個進行調用    promise.then(function(data){//每個promise成功之后,存放結果到result中,count--,直到所有promise被處理完了,才出發deferred的resolve方法,發布事件,傳遞結果出去      count--;      result[i] = data;      if(count === 0){        that.resolve(results);      }    },function(err){      that.reject(err);    });  });  return this.promise; //返回promise來讓外界偵聽這個deferred發布的事件。}var promise1 = readFile('foo.txt','utf-8');//這里的文件讀取已經經過promise化var promise2 = readFile('bar.txt','utf-8');var deferred = new Deferred();deferred.all([promise1,promise2]).thne(function(results){//promise1和promise2的then方法在deferred內部的all方法所調用,用于同步所有的promise  //TODO},function(err){  //TODO})

支持序列執行的Promise

嘗試改造一下代碼以實現鏈式調用

var Deferred = function(){  this.promise = new Promise()}//完成態Deferred.prototype.resolve = function(obj){  var promise = this.promise;  var handler;  while((handler = promise.queue.shift())){    if(handler && handler.fulfilled){      var ret = handler.fulfilled(obj);      if(ret && ret.isPromise){        ret.queue = promise.queue;        this.promise = ret;        return;      }    }  }}//失敗態Deferred.prototype.reject = function(err){  var promise = this.promise;  var handler;  while((handler = promise.queue.shift())){    if(handler && handler.error){      var ret = handler.error(err);      if(ret && ret.isPromise){        ret.queue = promise.queue;        this.promise = ret;        return      }    }  }}//生成回調函數Deferred.prototype.callback = function(){  var that = this;  return function(err,file){    if(err){      return that.reject(err);    }    that.resolve(file)  }}var Promise = function(){  this.queue = []; //隊列用于存儲待執行的回到函數  this.isPromise = true;};Promise.prototype.then = function(fulfilledHandler,errorHandler,progressHandler){  var handler = {};  if(typeof fulfilledHandler === 'function'){    handler.fulfilled = fulfilledHandler;  }  if(typeof errorHandler === 'function'){    handler.error = errorHandler;  }  this.queue.push(handler);  return this;}var readFile1 = function(file,encoding){  var deferred = new Deferred();  fs.readFile(file,encoding,deferred.callback());  return deferred.promise;}var readFile2 = function(file,encoding){  var deferred = new Deferred();  fs.readFile(file,encoding,deferred.callback());  return deferred.promise;}readFile1('file1.txt','utf8').then(function(file1){  return readFile2(file1.trim(),'utf8')}).then(function(file2){  console.log(file2)})

流程控制庫另外進行總結

參考《深入淺出node.js》一書,想學學習可以下載電子書

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 西乌珠穆沁旗| 仁寿县| 桐庐县| 翁源县| 莫力| 铜山县| 加查县| 金平| 长海县| 香格里拉县| 三明市| 丹棱县| 宁蒗| 静安区| 类乌齐县| 鄂托克前旗| 中江县| 綦江县| 永宁县| 新干县| 迁西县| 图们市| 射洪县| 紫云| 本溪市| 桐庐县| 迁安市| 丰宁| 天津市| 汉阴县| 香格里拉县| 天祝| 大邑县| 抚宁县| 马山县| 乌兰察布市| 陵水| 双鸭山市| 台前县| 靖安县| 尉氏县|