¿Cómo puedo encadenar estas funciones junto con las promesas?

Este es un progtwig que raspa los datos de un sitio web de camisetas y luego escribe la información del producto en un archivo CSV.

Hay 3 funciones de raspado y 1 función de escritura.

En este momento, estoy teniendo una pesadilla absoluta tratando de entender cómo implementar las promesas aquí sin necesidad de bibliotecas o paquetes de terceros. ¿Es esto posible solo con las características nativas de ES6?

Debido a la naturaleza asincrónica de las solicitudes, necesito que cada función y sus solicitudes finalicen por completo antes de llamar a la siguiente. Esto es para que pueda usar las variables como urlSet en la siguiente función.

¿Cómo puedo hacer esto simplemente sin reescribir mi código completo?

Debo mencionar que cada una de estas funciones funciona de forma individual, todas han sido probadas varias veces.

¿Cada función se convierte en una promesa individual?

El código está abajo, gracias:

 //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; }); } 

Si quieres encadenar esas funciones con promesas, entonces tienen que devolver las promesas .

Si desea encadenarlos con un módulo async , entonces tienen que tomar devoluciones de llamada como argumentos.

En este momento , ni devuelven una promesa (ni nada), ni toman devoluciones de llamada (o cualquier otra cosa) como argumentos. Si la función no recibe una callback y no devuelve nada, todo lo que puede hacer es llamar y eso es todo. No se le notificará ningún resultado.

Ejemplo

Devolución de llamada

Si tiene 3 funciones que toman devoluciones de llamada:

 function fun1(cb) { setTimeout(() => { cb(null, "fun1"); }, 1000); } function fun2(cb) { setTimeout(() => { cb(null, "fun2"); }, 3000); } function fun3(cb) { setTimeout(() => { cb(null, "fun3"); }, 100); } 

Entonces puedes saber cuándo terminan:

 fun3((err, value) => { console.log('fun3 finished:', value); }); 

Y puede esperar fácilmente uno antes de comenzar el otro:

 fun1((err1, val1) => { fun2((err2, val2) => { console.log("fun1 + fun2:", val1, val2); }); }); 

Promesas

Si sus funciones devuelven promesas:

 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); }); } 

Entonces también puedes saber cuándo terminan:

 fun3().then(value => { console.log('fun3 finished:', value); }); 

También puede anidar fácilmente las llamadas:

 fun1().then(val1 => { fun2().then(val2 => { console.log("fun1 + fun2:", val1, val2); }); }); 

O:

 fun1() .then(val1 => fun2()) .then(val2 => fun3()) .then(val3 => console.log('All 3 finished in series')); 

etc.

Para poder hacer mucho más con ambos estilos, consulte la documentación para: