前言
在沒有深度使用函數回調的經驗的時候,去看這些內容還是有一點吃力的。由于Node.js獨特的異步特性,才出現了“回調地獄”的問題,這篇文章中,我比較詳細的記錄了如何解決異步流問題。
文章會很長,而且這篇是對異步流模式的解釋。文中會使用一個簡單的網絡蜘蛛的例子,它的作用是抓取指定URL的網頁內容并保存在項目中,在文章的最后,可以找到整篇文章中的源碼demo。
1.原生JavaScript模式
本篇不針對初學者,因此會省略掉大部分的基礎內容的講解:
(spider_v1.js)
const request = require("request");const fs = require("fs");const mkdirp = require("mkdirp");const path = require("path");const utilities = require("./utilities");function spider(url, callback) { const filename = utilities.urlToFilename(url); console.log(`filename: ${filename}`); fs.exists(filename, exists => { if (!exists) { console.log(`Downloading ${url}`); request(url, (err, response, body) => { if (err) { callback(err); } else { mkdirp(path.dirname(filename), err => { if (err) { callback(err); } else { fs.writeFile(filename, body, err => { if (err) { callback(err); } else { callback(null, filename, true); } }); } }); } }); } else { callback(null, filename, false); } });}spider(process.argv[2], (err, filename, downloaded) => { if (err) { console.log(err); } else if (downloaded) { console.log(`Completed the download of ${filename}`); } else { console.log(`${filename} was already downloaded`); }});上邊的代碼的流程大概是這樣的:
這是一個非常簡單版本的蜘蛛,他只能抓取一個url的內容,看到上邊的回調多么令人頭疼。那么我們開始進行優化。
首先,if else 這種方式可以進行優化,這個很簡單,不用多說,放一個對比效果:
/// beforeif (err) { callback(err);} else { callback(null, filename, true);}/// afterif (err) { return callback(err);}callback(null, filename, true);代碼這么寫,嵌套就會少一層,但經驗豐富的程序員會認為,這樣寫過重強調了error,我們編程的重點應該放在處理正確的數據上,在可讀性上也存在這樣的要求。
另一個優化是函數拆分,上邊代碼中的spider函數中,可以把下載文件和保存文件拆分出去。
(spider_v2.js)
const request = require("request");const fs = require("fs");const mkdirp = require("mkdirp");const path = require("path");const utilities = require("./utilities");function saveFile(filename, contents, callback) { mkdirp(path.dirname(filename), err => { if (err) { return callback(err); } fs.writeFile(filename, contents, callback); });}function download(url, filename, callback) { console.log(`Downloading ${url}`); request(url, (err, response, body) => { if (err) { return callback(err); } saveFile(filename, body, err => { if (err) { return callback(err); } console.log(`Downloaded and saved: ${url}`); callback(null, body); }); })}function spider(url, callback) { const filename = utilities.urlToFilename(url); console.log(`filename: ${filename}`); fs.exists(filename, exists => { if (exists) { return callback(null, filename, false); } download(url, filename, err => { if (err) { return callback(err); } callback(null, filename, true); }) });}spider(process.argv[2], (err, filename, downloaded) => { if (err) { console.log(err); } else if (downloaded) { console.log(`Completed the download of ${filename}`); } else { console.log(`${filename} was already downloaded`); }});
新聞熱點
疑難解答
圖片精選