Maison > interface Web > js tutoriel > Explication détaillée du mécanisme de programmation asynchrone Node

Explication détaillée du mécanisme de programmation asynchrone Node

小云云
Libérer: 2018-01-09 17:17:37
original
1339 Les gens l'ont consulté

Cet article présente principalement le mécanisme de programmation asynchrone Node. L'éditeur le trouve plutôt bon, je vais donc le partager avec vous maintenant et le donner comme référence. Suivons l'éditeur pour y jeter un œil, j'espère que cela pourra aider tout le monde.

Cet article présente la programmation asynchrone Node et le partage avec tout le monde. Les détails sont les suivants :

Les principales solutions actuelles pour la programmation asynchrone sont :

  • Déclenchement d'événement/Mode abonnement

  • Mode Promesse/Différé

  • Bibliothèque de contrôle de processus

Mode de publication/abonnement d'événements

Node lui-même fournit le module d'événements, qui peut facilement implémenter la publication/abonnement d'événements


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

Les auditeurs peuvent être facilement ajoutés et supprimés de manière flexible, ce qui facilite l'association et le découplage des événements d'une logique de traitement spécifique

Le modèle de publication/abonnement d'événements est souvent utilisé pour dissocier la logique métier, et les éditeurs d'événements n'en ont pas besoin pour faire attention aux abonnements Comment l'auditeur implémente la logique métier, vous n'avez même pas besoin de prêter attention au nombre d'auditeurs existants, les données peuvent être transférées de manière flexible via des messages.

Le HTTP suivant est un scénario d'application typique


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

Si un événement ajoute plus de 10 auditeurs, vous recevrez un avertissement, cette restriction peut être supprimée en appelant emmite.setMaxListeners(0)

Module d'événements hérités


var events = require('events');
function Stream(){
  events.EventEmiiter.call(this);
}
util.inherits(Stream,events.EventEmitter);
Copier après la connexion

Utiliser file d'attente d'événements pour résoudre le problème d'avalanche

Le soi-disant problème d'avalanche est l'échec du cache dans des conditions de volume d'accès élevé et de grande concurrence. À l'heure actuelle, un grand nombre de requêtes sont intégrées dans le. base de données en même temps, et la base de données ne peut pas résister à une demande de requête aussi volumineuse en même temps, ce qui affectera davantage la vitesse de réponse globale du site Web

Solution :


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';//重新定义为已准备状态
    })
  }
}
Copier après la connexion

Schéma de collaboration entre plusieurs asynchrones

La relation entre les événements et les auditeurs dans les situations ci-dessus est de un à plusieurs, mais dans la programmation asynchrone, les événements et les auditeurs apparaîtront également dans plusieurs situations.

Voici une brève introduction utilisant la lecture de modèles, la lecture de données et la lecture de ressources localisées requises pour afficher la page à titre d'exemple


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)
})
Copier après la connexion

Solution de fonction partielle


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)
})
Copier après la connexion

Présentation de la solution de module 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);
})
Copier après la connexion

Mode promesse/différé

Lors de l'utilisation d'événements de la manière ci-dessus, le processus d'exécution doit être prédéfini, qui est déterminé par le mécanisme de fonctionnement de la publication/ mode d'abonnement de.


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

Alors, existe-t-il un moyen d'exécuter d'abord l'appel asynchrone et de retarder le traitement de la livraison ? La prochaine chose à aborder est la manière de gérer cette situation : le modèle Promesse/Différé

Promesse/A

Promesse/Une proposition fait cela pour un seul asynchrone opération Définition abstraite :

  • L'opération promise ne sera que dans l'un des trois états suivants : état inachevé, état terminé et état échoué.

  • L'état de promesse ne se transformera que d'inachevé en terminé ou en échec, et ne peut pas être inversé. Les états terminé et échoué ne peuvent pas être convertis l'un dans l'autre

  • .

    Une fois l’état de Promesse converti, il ne peut plus être modifié.

Un objet Promise doit seulement avoir then()

  • Accepter les méthodes de complétion et de rappel d'erreur

  • Prend en charge en option le rappel d'événement de progression comme troisième méthode

  • La méthode Then() n'accepte que les objets fonction, le reste des objets sera ignoré

  • La méthode Then() continue de renvoyer l'objet Promise pour implémenter les appels en chaîne

Simuler une implémentation de Promise via le module d'événements de 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;
}
Copier après la connexion

Ce qui précède utilise then() pour stocker la fonction de rappel. L'étape suivante consiste à attendre que les événements de réussite, d'erreur et de progression soient déclenchés. L'objet qui implémente cette fonction est appelé un objet différé, qui est un. objet de retard.


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)
}
Copier après la connexion

Par conséquent, un objet de réponse typique peut être encapsulé


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

et converti en


res.then(function(){
  //done
},function(err){
  //error
},function(chunk){
  console.log('Body:' + chunk);
})
Copier après la connexion

Pour terminer la conversion ci-dessus, vous devez d'abord encapsuler l'objet res et promettre les données, la fin, l'erreur et d'autres événements


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);
})
Copier après la connexion

Ci-dessus, il encapsule la partie immuable de l'entreprise dans Deferred et remet la partie variable à Promise

Collaboration asynchrone multiple dans 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
})
Copier après la connexion

Promesse qui prend en charge l'exécution de séquences

Essayez de modifier le code pour implémenter des appels enchaînés


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)
})
Copier après la connexion

Recommandations associées :

Introduction à 4 méthodes de programmation asynchrone en Javascript

Programmation asynchrone en es6 Explication de la promesse

Fonction de rappel de programmation asynchrone Javascript et exemples d'utilisation du gestionnaire explication détaillée

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal