Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erläuterung des asynchronen Node-Programmiermechanismus

Detaillierte Erläuterung des asynchronen Node-Programmiermechanismus

小云云
小云云Original
2018-01-09 17:17:371237Durchsuche

In diesem Artikel wird hauptsächlich der Mechanismus der asynchronen Programmierung in Node vorgestellt. Der Herausgeber findet ihn recht gut, daher werde ich ihn jetzt mit Ihnen teilen und als Referenz verwenden. Folgen wir dem Herausgeber und schauen wir uns das an. Ich hoffe, es kann allen helfen.

Dieser Artikel stellt die asynchrone Programmierung von Knoten vor und teilt sie mit allen. Die Details sind wie folgt:

Die aktuellen Hauptlösungen für die asynchrone Programmierung sind:

  • Ereignisfreigabe/Abonnementmodus

  • Versprechens-/Verzögerter Modus

  • Prozesssteuerungsbibliothek

Veranstaltungsveröffentlichungs-/Abonnementmodus

Node selbst stellt das Veranstaltungsmodul bereit, mit dem die Veröffentlichung/das Abonnement von Veranstaltungen problemlos implementiert werden kann


//订阅
emmiter.on("event1",function(message){
  console.log(message);
})
//发布
emmiter.emit("event1","I am mesaage!");

Listener können problemlos hinzugefügt und gelöscht werden, sodass Ereignisse einfach mit einer bestimmten Verarbeitungslogik verknüpft und von dieser entkoppelt werden können.

Das Veröffentlichungs-/Abonnementmodell für Ereignisse wird häufig zum Entkoppeln der Geschäftslogik verwendet, was für Ereignisherausgeber nicht erforderlich ist Auf Abonnements achten Wie implementieren Listener die Geschäftslogik? Sie müssen nicht einmal darauf achten, wie viele Listener Daten flexibel durch Nachrichten übertragen werden können.

Das folgende HTTP ist ein typisches Anwendungsszenario


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

Wenn ein Ereignis mehr als 10 Listener hinzufügt, erhalten Sie eine Warnung. Diese Einschränkung kann durch Aufrufen von emmite.setMaxListeners(0)

Inherits events module


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

Use aufgehoben werden Ereigniswarteschlange zur Lösung des Lawinenproblems

Das sogenannte Lawinenproblem ist der Cache-Fehler unter der Bedingung eines hohen Zugriffsvolumens und einer großen Parallelität. Zu diesem Zeitpunkt ist eine große Anzahl von Anforderungen integriert Datenbank gleichzeitig, und die Datenbank kann einer so großen Abfrageanforderung nicht gleichzeitig standhalten, was sich weiter auf die Gesamtantwortgeschwindigkeit der Website auswirkt

Lösung:


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';//重新定义为已准备状态
    })
  }
}

Kollaborationsschema zwischen mehreren Asynchronen

Die Beziehung zwischen Ereignissen und Zuhörern in den oben genannten Situationen ist eins-zu-viele, aber in Bei der asynchronen Programmierung werden auch Ereignisse und Zuhörer in vielen Situationen angezeigt.

Hier ist eine kurze Einführung unter Verwendung des Vorlagenlesens, des Datenlesens und des lokalisierten Ressourcenlesens, die zum Rendern der Seite als Beispiel erforderlich sind


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)
})

Teilfunktionslösung


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)
})

Einführung der EventProxy-Modullösung


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-Modus

Wenn Ereignisse auf die oben beschriebene Weise verwendet werden, muss der Ausführungsprozess voreingestellt werden, der durch den Betriebsmechanismus des Veröffentlichungs-/ Abonnementmodus von.


$.get('/api',{
  success:onSuccess,
  err:onError,
  complete:onComplete
})
//需要严谨设置目标

Gibt es also eine Möglichkeit, zuerst den asynchronen Aufruf auszuführen und die Lieferverarbeitung zu verzögern? Das nächste, worüber wir sprechen müssen, ist die Art und Weise, mit dieser Situation umzugehen: Promise/Deferred-Muster

Promise/A

Promise/A-Vorschlag führt dies für einen einzelnen Asynchronen aus Operation Abstrakte Definition:

  • Promise-Operation wird nur in einem von drei Zuständen sein: unvollendeter Zustand, abgeschlossener Zustand und fehlgeschlagener Zustand.

  • Der Status „Versprechen“ ändert sich nur von „unvollständig“ in „abgeschlossen“ oder „fehlgeschlagen“ und kann nicht rückgängig gemacht werden. Abgeschlossene und fehlgeschlagene Zustände können nicht ineinander umgewandelt werden

  • Sobald der Status „Versprechen“ umgewandelt wurde, kann er nicht mehr geändert werden.

Ein Promise-Objekt muss nur then() haben

  • Abschluss- und Fehlerrückrufmethoden akzeptieren

  • Unterstützt optional den Rückruf von Fortschrittsereignissen als dritte Methode

  • Die Methode Then() akzeptiert nur Funktionsobjekte, der Rest der Objekte wird ignoriert

  • Then()-Methode gibt weiterhin das Promise-Objekt zurück, um Kettenaufrufe zu implementieren

Simulieren Sie eine Promise-Implementierung über das Ereignismodul von Node


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;
}

Das Obige verwendet then(), um die Rückruffunktion zu speichern. Der nächste Schritt besteht darin, auf die Auslösung der Erfolgs-, Fehler- und Fortschrittsereignisse zu warten. Das Objekt, das diese Funktion implementiert, wird als verzögertes Objekt bezeichnet Verzögerungsobjekt.


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)
}

Daher kann ein typisches Antwortobjekt


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

gekapselt und in


res.then(function(){
  //done
},function(err){
  //error
},function(chunk){
  console.log('Body:' + chunk);
})
Um die obige Konvertierung abzuschließen, müssen Sie zunächst das res-Objekt kapseln und die Daten, das Ende, den Fehler und andere Ereignisse versprechen


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);
})
Oben kapselt es den unveränderlichen Teil des Geschäfts in Deferred und übergibt den variablen Teil an Promise

Mehrfache asynchrone Zusammenarbeit in 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
})

Versprechen, das die Sequenzausführung unterstützt

Versuchen Sie, den Code zu ändern, um verkettete Aufrufe zu implementieren


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)
})
Verwandte Empfehlungen:

Einführung in 4 Methoden der asynchronen Programmierung in Javascript

Asynchrone Programmierung in es6 Versprechen Erklärung

Ausführliche Erläuterung der Callback-Funktion für die asynchrone Javascript-Programmierung und Beispiele für die Verwendung von Managern

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des asynchronen Node-Programmiermechanismus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn