如何将这些功能与承诺链接在一起? [英] How can I chain these functions together with promises?

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

问题描述

这是一个从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个scrape函数和1个写入功能。

There are 3 scrape functions and 1 write function.

现在,我正在寻找一个绝对的噩梦试图在没有任何第三方库或包的情况下实现承诺。只有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;
    });
}


推荐答案

如果你想链接那些功能与承诺,然后他们必须返回承诺

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:

  • http://caolan.github.io/async/
  • http://bluebirdjs.com/docs/getting-started.html
  • https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise

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

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