<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 目標 建立一個 lesson4 項目,在其中編寫代碼。 代碼的入口是?`app.js`,當調用?`node app.js`?時,它會輸出 CNode([https://cnodejs.org/](https://cnodejs.org/)?) 社區首頁的所有主題的標題,鏈接和第一條評論,以 json 的格式。 輸出示例: ~~~ [ { "title": "【公告】發招聘帖的同學留意一下這里", "href": "http://cnodejs.org/topic/541ed2d05e28155f24676a12", "comment1": "呵呵呵呵" }, { "title": "發布一款 Sublime Text 下的 JavaScript 語法高亮插件", "href": "http://cnodejs.org/topic/54207e2efffeb6de3d61f68f", "comment1": "沙發!" } ] ~~~ ## [](https://github.com/alsotang/node-lessons/tree/master/lesson4#挑戰)挑戰 以上文目標為基礎,輸出?`comment1`?的作者,以及他在 cnode 社區的積分值。 示例: ~~~ [ { "title": "【公告】發招聘帖的同學留意一下這里", "href": "http://cnodejs.org/topic/541ed2d05e28155f24676a12", "comment1": "呵呵呵呵", "author1": "auser", "score1": 80 }, ... ] ~~~ ## [](https://github.com/alsotang/node-lessons/tree/master/lesson4#知識點)知識點 1. 體會 Node.js 的 callback hell 之美 2. 學習使用 eventproxy 這一利器控制并發 ## [](https://github.com/alsotang/node-lessons/tree/master/lesson4#課程內容)課程內容 這一章我們來到了 Node.js 最牛逼的地方——異步并發的內容了。 上一課我們介紹了如何使用 superagent 和 cheerio 來取主頁內容,那只需要發起一次 http get 請求就能辦到。但這次,我們需要取出每個主題的第一條評論,這就要求我們對每個主題的鏈接發起請求,并用 cheerio 去取出其中的第一條評論。 CNode 目前每一頁有 40 個主題,于是我們就需要發起 1 + 40 個請求,來達到我們這一課的目標。 后者的 40 個請求,我們并發地發起:),而且不會遇到多線程啊鎖什么的,Node.js 的并發模型跟多線程不同,拋卻那些觀念。更具體一點的話,比如異步到底為何異步,Node.js 為何單線程卻能并發這類走近科學的問題,我就不打算講了。對于這方面有興趣的同學,強烈推薦 @樸靈 的 《九淺一深Node.js》:?[http://book.douban.com/subject/25768396/](http://book.douban.com/subject/25768396/)?。 有些逼格比較高的朋友可能聽說過 promise 和 generator 這類概念。不過我呢,只會講 callback,主要原因是我個人只喜歡 callback。 這次課程我們需要用到三個庫:superagent cheerio eventproxy([https://github.com/JacksonTian/eventproxy](https://github.com/JacksonTian/eventproxy)?) 手腳架的工作各位自己來,我們一步一步來一起寫出這個程序。 首先 app.js 應該長這樣 ~~~ 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); }); ~~~ 運行?`node app.js` 輸出如下圖: ![2015-08-03/55bf1aec4012b](https://box.kancloud.cn/2015-08-03_55bf1aec4012b.png) OK,這時候我們已經得到所有 url 的地址了,接下來,我們把這些地址都抓取一遍,就完成了,Node.js 就是這么簡單。 抓取之前,還是得介紹一下 eventproxy 這個庫。 用 js 寫過異步的同學應該都知道,如果你要并發異步獲取兩三個地址的數據,并且要在獲取到數據之后,對這些數據一起進行利用的話,常規的寫法是自己維護一個計數器。 先定義一個?`var count = 0`,然后每次抓取成功以后,就?`count++`。如果你是要抓取三個源的數據,由于你根本不知道這些異步操作到底誰先完成,那么每次當抓取成功的時候,就判斷一下?`count === 3`。當值為真時,使用另一個函數繼續完成操作。 而 eventproxy 就起到了這個計數器的作用,它來幫你管理到底這些異步操作是否完成,完成之后,它會自動調用你提供的處理函數,并將抓取到的數據當參數傳過來。 假設我們不使用 eventproxy 也不使用計數器時,抓取三個源的寫法是這樣的: ~~~ // 參考 jquery 的 $.get 的方法 $.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); }); }); }); ~~~ 上述的代碼大家都寫過吧。先獲取 data1,獲取完成之后獲取 data2,然后再獲取 data3,然后 fuck 它們,進行輸出。 但大家應該也想到了,其實這三個源的數據,是可以并行去獲取的,data2 的獲取并不依賴 data1 的完成,data3 同理也不依賴 data2。 于是我們用計數器來寫,會寫成這樣: ~~~ (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); } } })(); ~~~ ~~丑的一逼,~~也不算丑,主要我寫代碼好看。 如果我們用 eventproxy,寫出來是這樣的: ~~~ 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); }); ~~~ 好看多了是吧,也就是個高等計數器嘛。 `ep.all('data1_event', 'data2_event', 'data3_event', function (data1, data2, data3) {});` 這一句,監聽了三個事件,分別是?`data1_event, data2_event, data3_event`,每次當一個源的數據抓取完成時,就通過`ep.emit()`?來告訴?`ep`?自己,某某事件已經完成了。 當三個事件未同時完成時,`ep.emit()`?調用之后不會做任何事;當三個事件都完成的時候,就會調用末尾的那個回調函數,來對它們進行統一處理。 eventproxy 提供了不少其他場景所需的 API,但最最常用的用法就是以上的這種,即: 1. 先?`var ep = new eventproxy();`?得到一個 eventproxy 實例。 2. 告訴它你要監聽哪些事件,并給它一個回調函數。`ep.all('event1', 'event2', function (result1, result2) {})`。 3. 在適當的時候?`ep.emit('event_name', eventData)`。 eventproxy 這套處理異步并發的思路,我一直覺得就像是匯編里面的 goto 語句一樣,程序邏輯在代碼中隨處跳躍。本來代碼已經執行到 100 行了,突然 80 行的那個回調函數又開始工作了。如果你異步邏輯復雜點的話,80 行的這個函數完成之后,又激活了 60 行的另外一個函數。并發和嵌套的問題雖然解決了,但老祖宗們消滅了幾十年的 goto 語句又回來了。 至于這套思想糟糕不糟糕,我個人倒是覺得還是不糟糕,用熟了看起來蠻清晰的。不過 js 這門渣渣語言本來就亂嘛,什么變量提升([http://www.cnblogs.com/damonlan/archive/2012/07/01/2553425.html](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](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]); }); }); ~~~ 輸出長這樣: ![2015-08-03/55bf1acb23d5a](https://box.kancloud.cn/2015-08-03_55bf1acb23d5a.png) 完整的代碼請查看 lesson4 目錄下的 app.js 文件
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看