Heim > Web-Frontend > js-Tutorial > Nodejs praktische Erfahrung: Das Eventproxy-Modul steuert concurrency_node.js

Nodejs praktische Erfahrung: Das Eventproxy-Modul steuert concurrency_node.js

WBOY
Freigeben: 2016-05-16 15:34:59
Original
1693 Leute haben es durchsucht

Ziel

Erstellen Sie ein Lektion4-Projekt und schreiben Sie Code darin.

Der Einstiegspunkt des Codes ist app.js. Wenn der Knoten app.js aufgerufen wird, werden die Titel, Links und Kapitel aller Themen auf dem CNode ausgegeben (https://cnodejs.org/) Community-Homepage. Ein Kommentar im JSON-Format.

Ausgabebeispiel:

[
 {
  "title": "【公告】发招聘帖的同学留意一下这里",
  "href": "http://cnodejs.org/topic/541ed2d05e28155f24676a12",
  "comment1": "呵呵呵呵"
 },
 {
  "title": "发布一款 Sublime Text 下的 JavaScript 语法高亮插件",
  "href": "http://cnodejs.org/topic/54207e2efffeb6de3d61f68f",
  "comment1": "沙发!"
 }
]
Nach dem Login kopieren

Herausforderung

Geben Sie basierend auf dem oben genannten Ziel den Autor von Kommentar1 und seinen Punktewert in der Cnode-Community aus.

Beispiel:

[
 {
  "title": "【公告】发招聘帖的同学留意一下这里",
  "href": "http://cnodejs.org/topic/541ed2d05e28155f24676a12",
  "comment1": "呵呵呵呵",
  "author1": "auser",
  "score1": 80
 },
 ...
]
Nach dem Login kopieren

Wissenspunkte

Erleben Sie die Schönheit der Callback-Hölle von Node.js

Lernen Sie, Eventproxy zu verwenden, ein Tool zur Steuerung der Parallelität

Kursinhalte

In diesem Kapitel kommen wir zum großartigsten Teil von Node.js – der asynchronen Parallelität.

In der vorherigen Lektion haben wir vorgestellt, wie man Superagent und Cheerio verwendet, um den Inhalt der Homepage abzurufen. Es muss lediglich eine http-Get-Anfrage initiiert werden. Aber dieses Mal müssen wir den ersten Kommentar jedes Themas abrufen, was erfordert, dass wir eine Anfrage für den Link jedes Themas initiieren und cheerio verwenden, um den ersten Kommentar abzurufen.

CNode hat derzeit 40 Themen pro Seite, daher müssen wir 1 40 Anfragen initiieren, um unser Ziel in dieser Lektion zu erreichen.

Wir haben die letzten 40 Anfragen gleichzeitig initiiert :), und wir werden weder auf Multithreading noch auf Sperren stoßen. Das Parallelitätsmodell von Node.js unterscheidet sich vom Multithreading, also geben Sie diese Konzepte auf. Um genauer zu sein, werde ich nicht auf wissenschaftliche Fragen eingehen, etwa warum asynchron asynchron ist und warum Node.js Single-Threaded, aber gleichzeitig sein kann. Für Studierende, die sich für diesen Aspekt interessieren, kann ich @pulings „Nine Lights and One Deep Node.js“ wärmstens empfehlen: http://book.douban.com/subject/25768396/.

Einige Freunde, die anspruchsvoller sind, haben möglicherweise von Konzepten wie Versprechen und Generatoren gehört. Aber was mich betrifft, kann ich nur über Rückrufe sprechen. Der Hauptgrund ist, dass ich persönlich nur Rückrufe mag.

Wir müssen für diesen Kurs drei Bibliotheken verwenden: superagent cheerio eventproxy(https://github.com/JacksonTian/eventproxy )
Die Gerüstbauarbeiten können Sie selbst durchführen, wir schreiben gemeinsam Schritt für Schritt das Programm.

Erste app.js sollten so aussehen

var eventproxy = require('eventproxy');
var superagent = require('superagent');
var cheerio = require('cheerio');
// url 模块是 Node.js 标准库里面的
// http://nodejs.org/api/url.html
var url = require('url');
var cnodeUrl = 'https://cnodejs.org/';
superagent.get(cnodeUrl)
 .end(function (err, res) {
  if (err) {
   return console.error(err);
  }
  var topicUrls = [];
  var $ = cheerio.load(res.text);
  // 获取首页所有的链接
  $('#topic_list .topic_title').each(function (idx, element) {
   var $element = $(element);
   // $element.attr('href') 本来的样子是 /topic/542acd7d5d28233425538b04
   // 我们用 url.resolve 来自动推断出完整 url,变成
   // https://cnodejs.org/topic/542acd7d5d28233425538b04 的形式
   // 具体请看 http://nodejs.org/api/url.html#url_url_resolve_from_to 的示例
   var href = url.resolve(cnodeUrl, $element.attr('href'));
   topicUrls.push(href);
  });
  console.log(topicUrls);
 });

Nach dem Login kopieren

Node app.js ausführen

Die Ausgabe sieht wie folgt aus:

OK, jetzt haben wir die Adressen aller URLs, wir crawlen alle diese Adressen und fertig ist Node.js.
Vor dem Crawlen müssen wir noch die Eventproxy-Bibliothek vorstellen.

Studenten, die asynchron in js geschrieben haben, sollten alle wissen, dass, wenn Sie Daten von zwei oder drei Adressen gleichzeitig und asynchron abrufen und diese Daten nach Erhalt der Daten zusammen verwenden möchten, die herkömmliche Schreibweise darin besteht, eine selbst zu verwalten Schalter.

Definieren Sie zunächst eine Variablenanzahl = 0 und zählen Sie dann jedes Mal, wenn der Crawl erfolgreich ist. Wenn Sie Daten aus drei Quellen erfassen möchten und nicht wissen, wer diese asynchronen Vorgänge zuerst abschließt, überprüfen Sie bei jeder erfolgreichen Erfassung die Anzahl === 3. Wenn der Wert wahr ist, verwenden Sie eine andere Funktion, um den Vorgang fortzusetzen.
Eventproxy übernimmt die Rolle dieses Zählers. Er hilft Ihnen bei der Verwaltung, ob diese asynchronen Vorgänge abgeschlossen sind. Nach Abschluss ruft er automatisch die von Ihnen bereitgestellte Verarbeitungsfunktion auf und übergibt die erfassten Daten als Parameter.
Unter der Annahme, dass wir keinen Eventproxy oder Zähler verwenden, können drei Quellen wie folgt erfasst werden:

// Siehe die $.get-Methode von jquery

$.get("http://data1_source", function (data1) {
 // something
 $.get("http://data2_source", function (data2) {
  // something
  $.get("http://data3_source", function (data3) {
   // something
   var html = fuck(data1, data2, data3);
   render(html);
  });
 });
});

Nach dem Login kopieren

Jeder hat den obigen Code geschrieben. Holen Sie sich zuerst Daten1, dann Daten2, dann Daten3, dann ficken Sie sie und geben Sie sie aus.

Aber jeder hätte denken sollen, dass die Daten aus diesen drei Quellen tatsächlich parallel bezogen werden können. Die Erfassung von Daten2 hängt nicht von der Vervollständigung von Daten1 ab, und ebenso hängt Daten3 nicht von Daten2 ab.

Also verwenden wir einen Zähler, um es zu schreiben, und es wird so geschrieben:

(function () {
 var count = 0;
 var result = {};
 $.get('http://data1_source', function (data) {
  result.data1 = data;
  count++;
  handle();
  });
 $.get('http://data2_source', function (data) {
  result.data2 = data;
  count++;
  handle();
  });
 $.get('http://data3_source', function (data) {
  result.data3 = data;
  count++;
  handle();
  });
 function handle() {
  if (count === 3) {
   var html = fuck(result.data1, result.data2, result.data3);
   render(html);
  }
 }
})();
Nach dem Login kopieren

Auch wenn es hässlich ist, ist es nicht so hässlich. Die Hauptsache ist, dass der Code, den ich schreibe, gut aussieht.

Wenn wir Eventproxy verwenden, würde es so geschrieben werden:

var ep = new eventproxy();
ep.all('data1_event', 'data2_event', 'data3_event', function (data1, data2, data3) {
 var html = fuck(data1, data2, data3);
 render(html);
});
$.get('http://data1_source', function (data) {
 ep.emit('data1_event', data);
 });
$.get('http://data2_source', function (data) {
 ep.emit('data2_event', data);
 });
$.get('http://data3_source', function (data) {
 ep.emit('data3_event', data);
 });
Nach dem Login kopieren

Es sieht viel besser aus, oder? Es ist nur ein fortschrittlicher Zähler.

ep.all('data1_event', 'data2_event', 'data3_event', function (data1, data2, data3) {});
Nach dem Login kopieren

这一句,监听了三个事件,分别是 data1_event, data2_event, data3_event,每次当一个源的数据抓取完成时,就通过 ep.emit() 来告诉 ep 自己,某某事件已经完成了。

当三个事件未同时完成时,ep.emit() 调用之后不会做任何事;当三个事件都完成的时候,就会调用末尾的那个回调函数,来对它们进行统一处理。

eventproxy 提供了不少其他场景所需的 API,但最最常用的用法就是以上的这种,即:

先 var ep = new eventproxy(); 得到一个 eventproxy 实例。

告诉它你要监听哪些事件,并给它一个回调函数。ep.all('event1', 'event2', function (result1, result2) {})。
在适当的时候 ep.emit('event_name', eventData)。

eventproxy 这套处理异步并发的思路,我一直觉得就像是汇编里面的 goto 语句一样,程序逻辑在代码中随处跳跃。本来代码已经执行到 100 行了,突然 80 行的那个回调函数又开始工作了。如果你异步逻辑复杂点的话,80 行的这个函数完成之后,又激活了 60 行的另外一个函数。并发和嵌套的问题虽然解决了,但老祖宗们消灭了几十年的 goto 语句又回来了。

至于这套思想糟糕不糟糕,我个人倒是觉得还是不糟糕,用熟了看起来蛮清晰的。不过 js 这门渣渣语言本来就乱嘛,什么变量提升(http://www.cnblogs.com/damonlan/archive/2012/07/01/2553425.html )啊,没有 main 函数啊,变量作用域啊,数据类型常常简单得只有数字、字符串、哈希、数组啊,这一系列的问题,都不是事儿。
编程语言美丑啥的,咱心中有佛就好。

回到正题,之前我们已经得到了一个长度为 40 的 topicUrls 数组,里面包含了每条主题的链接。那么意味着,我们接下来要发出 40 个并发请求。我们需要用到 eventproxy 的 #after API。

大家自行学习一下这个 API 吧:https://github.com/JacksonTian/eventproxy#%E9%87%8D%E5%A4%8D%E5%BC%82%E6%AD%A5%E5%8D%8F%E4%BD%9C
我代码就直接贴了哈。

// 得到 topicUrls 之后
// 得到一个 eventproxy 的实例
var ep = new eventproxy();
// 命令 ep 重复监听 topicUrls.length 次(在这里也就是 40 次) `topic_html` 事件再行动
ep.after('topic_html', topicUrls.length, function (topics) {
 // topics 是个数组,包含了 40 次 ep.emit('topic_html', pair) 中的那 40 个 pair
 // 开始行动
 topics = topics.map(function (topicPair) {
  // 接下来都是 jquery 的用法了
  var topicUrl = topicPair[0];
  var topicHtml = topicPair[1];
  var $ = cheerio.load(topicHtml);
  return ({
   title: $('.topic_full_title').text().trim(),
   href: topicUrl,
   comment1: $('.reply_content').eq(0).text().trim(),
  });
 });
 console.log('final:');
 console.log(topics);
});
topicUrls.forEach(function (topicUrl) {
 superagent.get(topicUrl)
  .end(function (err, res) {
   console.log('fetch ' + topicUrl + ' successful');
   ep.emit('topic_html', [topicUrl, res.text]);
  });
});
Nach dem Login kopieren

输出长这样:

完整的代码请查看 lesson4 目录下的 app.js 文件

总结

今天介绍的 eventproxy 模块是控制并发用的,有时我们需要同时发送 N 个 http 请求,然后利用得到的数据进行后期的处理工作,如何方便地判断数据已经全部并发获取得到,就可以用到该模块了。而模块不仅可以在服务端使用,也可以应用在客户端

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage