该程序可将数据从T恤衫网站中抓取,然后将产品信息写入CSV文件。
有3个刮擦功能和1个写入功能。
现在,我陷入了一场绝对的噩梦,试图在没有任何第三方库或软件包的情况下实现如何在这里实现承诺。仅凭ES6的本机功能可以做到吗?
由于请求的异步性质,在调用下一个函数之前,我需要每个函数及其请求完全完成。这样,我就可以使用urlSet下一个函数中的变量。
urlSet
我怎么能简单地做到这一点而无需重写我的整个代码?
我应该提到,这些功能中的每个功能都是单独工作的,它们都经过了多次测试。
每个功能是否都会成为一个单独的承诺?
代码如下,谢谢:
//TASK: Create a command line application that goes to an ecommerce site to get the latest prices. //Save the scraped data in a spreadsheet (CSV format). 'use strict'; //Modules being used: var cheerio = require('cheerio'); var json2csv = require('json2csv'); var request = require('request'); var moment = require('moment'); var fs = require('fs'); //harcoded url var url = 'http://shirts4mike.com/'; //url for tshirt pages var urlSet = new Set(); var remainder; var tshirtArray = []; // First scrape loads front page of shirts4mike and finds the first product pages/menus function firstScrape(){ request(url, function(error, response, html) { if(!error && response.statusCode == 200){ var $ = cheerio.load(html); //iterate over links with 'shirt' $('a[href*=shirt]').each(function(){ var a = $(this).attr('href'); //create new link var scrapeLink = url + a; //for each new link, go in and find out if there is a submit button. //If there, add it to the set request(scrapeLink, function(error,response, html){ if(!error && response.statusCode == 200) { var $ = cheerio.load(html); //if page has a submit it must be a product page if($('[type=submit]').length !== 0){ //add page to set urlSet.add(scrapeLink); } else if(remainder == undefined) { //if not a product page, add it to remainder so it another scrape can be performed. remainder = scrapeLink; } } }); }); } }); } //Scrape next level of menus to find remaning product pages to add to urlSet function secondScrape() { request(remainder, function(error, response, html) { if(!error && response.statusCode == 200){ var $ = cheerio.load(html); $('a[href*=shirt]').each(function(){ var a = $(this).attr('href'); //create new link var scrapeLink = url + a; request(scrapeLink, function(error,response, html){ if(!error && response.statusCode == 200){ var $ = cheerio.load(html); //collect remaining product pages and add to set if($('[type=submit]').length !== 0){ urlSet.add(scrapeLink); } } }); }); } }); } //call lastScraper so we can grab data from the set (product pages) function lastScraper(){ //scrape set, product pages for(var item of urlSet){ var url = item; request(url, function(error, response, html){ if(!error && response.statusCode == 200){ var $ = cheerio.load(html); //grab data and store as variables var price = $('.price').text(); var imgURL = $('.shirt-picture').find('img').attr('src'); var title = $('body').find('.shirt-details > h1').text().slice(4); var tshirtObject = {}; //add values into tshirt object tshirtObject.Title = title; tshirtObject.Price = price; tshirtObject.ImageURL = imgURL; tshirtObject.URL = url; tshirtObject.Date = moment().format('MMMM Do YYYY, h:mm:ss a'); //add the object into the array of tshirts tshirtArray.push(tshirtObject); } }); } } //Convert array of tshirt objects and write to CSV file function convertJson2Csv(){ //The scraper should generate a folder called `data` if it doesn’t exist. var dir ='./data'; if(!fs.existsSync(dir)){ fs.mkdirSync(dir); } var fields = ['Title', 'Price', 'ImageURL', 'URL', 'Date']; //convert tshirt data into CSV and pass in fields var csv = json2csv({ data: tshirtArray, fields: fields }); //Name of file will be the date var fileDate = moment().format('MM-DD-YY'); var fileName = dir + '/' + fileDate + '.csv'; //Write file fs.writeFile(fileName, csv, {overwrite: true}, function(err) { console.log('file saved'); if (err) throw err; }); }
如果您想将这些函数与Promise链接在一起,则它们必须 返回Promise 。
如果要将它们与async模块链接,则它们必须 将回调 作为参数。
async
现在, 他们既不返回诺言 (或其他任何东西), 也不以回调 (或其他任何东西)为参数。如果该函数不执行回调并且不返回任何内容,则您只能调用它而已。您将不会收到任何结果的通知。
如果您有3个采用回调的函数:
function fun1(cb) { setTimeout(() => { cb(null, "fun1"); }, 1000); } function fun2(cb) { setTimeout(() => { cb(null, "fun2"); }, 3000); } function fun3(cb) { setTimeout(() => { cb(null, "fun3"); }, 100); }
然后,您可以知道何时完成:
fun3((err, value) => { console.log('fun3 finished:', value); });
您可以轻松地等待一个,然后再开始另一个:
fun1((err1, val1) => { fun2((err2, val2) => { console.log("fun1 + fun2:", val1, val2); }); });
如果函数返回promise:
function fun1() { return new Promise((res, rej) => { setTimeout(() => { res("fun1"); }, 1000); }); } function fun2() { return new Promise((res, rej) => { setTimeout(() => { res("fun2"); }, 3000); }); } function fun3() { return new Promise((res, rej) => { setTimeout(() => { res("fun3"); }, 100); }); }
然后,您还可以知道它们何时完成:
fun3().then(value => { console.log('fun3 finished:', value); });
您还可以轻松地嵌套调用:
fun1().then(val1 => { fun2().then(val2 => { console.log("fun1 + fun2:", val1, val2); }); });
要么:
fun1() .then(val1 => fun2()) .then(val2 => fun3()) .then(val3 => console.log('All 3 finished in series'));
等等
为了能够同时使用这两种样式,请参见以下文档: