如何将这些函数与 promise 链接在一起? [英] How can I chain these functions together with promises?

查看:25
本文介绍了如何将这些函数与 promise 链接在一起?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个从 T 恤网站上抓取数据,然后将产品信息写入 CSV 文件的程序.

This is a program that scrapes the data out of a tshirt website and then writes the product info to a CSV file.

有 3 个抓取函数和 1 个写入函数.

There are 3 scrape functions and 1 write function.

现在,我有一个绝对的噩梦,试图弄清楚如何在没有任何 3rd 方库或包的情况下在这里实现承诺.仅使用 ES6 的本机特性就可以做到这一点吗?

Right now, I am having an absolute nightmare trying to get my head around how to implement promises here without any 3rd party libraries or packages. Is this possible with just the native features of ES6?

由于请求的异步性质,我需要在调用下一个函数之前完全完成每个函数及其请求.这样我就可以在下一个函数中使用诸如 urlSet 之类的变量.

Due to the async nature of the requests, I need each function and its requests to finish completely before the next one is called. This is so I can use the variables such as urlSet in the next function.

如何在不重写整个代码的情况下简单地做到这一点?

How can I do this simply without rewriting my whole code?

我应该提到,这些功能中的每一个都可以单独运行,它们都已经过多次测试.

I should mention that each of these functions work on an individual basis, they've all been tested several times.

每个函数都成为一个单独的承诺吗?

Does each function become an individual promise?

代码如下,谢谢:

//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.

If you want to chain those functions with promises, then they have to return promises.

如果您想将它们与 async 模块链接起来,那么它们必须将回调作为参数.

If you want to chain them with async module, then they have to take callbacks as arguments.

现在他们既不返回承诺(或其他任何东西),也不将回调(或其他任何东西)作为参数.如果该函数不接受回调并且不返回任何内容,那么您所能做的就是调用它,仅此而已.您不会收到任何结果通知.

Right now they neither return a promise (or anything else), nor do they take callbacks (or anything else) as arguments. If the function doesn't take a callback and doesn't return anything then all you can do is call it and that's it. You will not be notified of any result.

如果您有 3 个需要回调的函数:

If you have 3 functions that take callbacks:

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

然后你就可以知道他们什么时候完成:

Then you can know when they finish:

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

而且您可以轻松地等待一个,然后再开始另一个:

And you can easily wait for one before you start the other:

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

承诺

如果您的函数返回承诺:

Promises

If your functions return promises:

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

然后你也可以知道他们什么时候完成:

Then you can also know when they finish:

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

您还可以轻松嵌套调用:

You can also easily nest the calls:

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

为了能够用这两种风格做更多的事情,请参阅以下文档:

To be able to do much more with both style, see documentation for:

这篇关于如何将这些函数与 promise 链接在一起?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆