然后()在Promise之前完成

时间:2018-10-02 12:06:47

标签: javascript node.js asynchronous promise es6-promise

我在这里遇到了JS Promises的一些基本问题。这是我的完整代码

'use strict'
const rp = require('request-promise');
const cheerio = require('cheerio');
var fs = require('fs');
var os = require('os');

const options = {
    uri: url,
    normalizeWhitespace: true,
    transform: function (body) {
        return cheerio.load(body);
    }
};

let results = []
let results2 = []

rp(options)
.then(($) => {
    $('.col-xs-4 .grid-item').each(function (i, elem) {
        let temp = $(this).find(".prod-image").attr("style")
        let productImageUrl = temp.substring(temp.indexOf("background-image:url('") + 22, temp.indexOf("')"))
        let detailUrl = $(this).find(".prod-image").attr("href")
        let title = $(this).find(".title").text()
        let description = $(elem).children().eq(4).attr("content")

        results.push({
            "productImageUrl": productImageUrl,
            "detailUrl": detailUrl,
            "title": title,
            "description": description
        })
    });
})
.then(() => {
    results.forEach(item => {
        const options1 = {
            uri: item.detailUrl,
            normalizeWhitespace: true,
            transform: function (body) {
                return cheerio.load(body);
            }
        };

        rp(options1)
            .then(($) => {
                console.log(5)
                let temp = $('#prod-title').text()
                let unit = temp.substring(temp.indexOf('Size: ') + 6, temp.indexOf('mL') - 1)
                let retail = temp.substring(temp.indexOf('Retail: $') + 9, temp.indexOf(' A'))
                let wholesale = temp.substring(temp.indexOf('Wholesale: $') + 12, temp.indexOf(' A') + 21)
                results2.push({
                    "productImageUrl": item.productImageUrl,
                    "detailUrl": item.detailUrl,
                    "title": item.title,
                    "description": item.description,
                    "unit": unit,
                    "retail": retail,
                    "wholesale": wholesale
                })
            })
            .catch((err) => {
                console.log(err);
            });
    })
})
.finally(() => {
    console.log("FINALLY " + results2)
    let header = "Handle,Title,Body" + os.EOL

    fs.writeFile("./file.csv", header, function (err) {
        if (err) {
            return console.log(err);
        }
    });

    for (let item of results2) {
        console.log(2)
        let hyphenateTitle = item.title.replace(/\s+/g, '-').toLowerCase();
        let line = hyphenateTitle + "," + item.title + "," + item.description + "," + vendor + ',"","",true,Title,Default Title,,,,,SKU,10000,,1,deny,manual,' + item.retail + "," + item.wholesale + "," + 'true,true,"",' + item.productImageUrl + "," + ',1,,false,,,,,,,,,,,,,,,,,kg,' + os.EOL
        fs.appendFile("./file.csv", line, function (err) {
            if (err) {
                return console.log(err);
            } else {
                // done
            }
        })
    }
})

.catch((err) => {
    console.log(err);
});

这个想法是,在第一个then()中,我将阅读HTML页面并找到一些URL。我会将这些信息推送到results数组中。

然后,在第二个then上,其想法是遍历数组中的每个项目并转到第二页,提取更多信息并将其推送到results

Finally,将全部信息保存在csv中。

这是控制台中的输出:

FINALLY 
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5
5

因此,您可以看到最后一个then在中间一个之前(但在最上面的一个之后)运行。

我在做什么错? 谢谢

编辑1 这是一个评论者建议的新代码:

'use strict'
const rp = require('request-promise');
const cheerio = require('cheerio');
var fs = require('fs');
var os = require('os');

const options = {
    uri: url,
    normalizeWhitespace: true,
    transform: function (body) {
        return cheerio.load(body);
    }
};

let results = []
let results2 = []

rp(options)
    .then(($) => {
        console.log("FIRST THEN")
        $('.col-xs-4 .grid-item').each(function (i, elem) {
            let temp = $(this).find(".prod-image").attr("style")
            let productImageUrl = temp.substring(temp.indexOf("background-image:url('") + 22, temp.indexOf("')"))
            let detailUrl = $(this).find(".prod-image").attr("href")
            let title = $(this).find(".title").text()
            let description = $(elem).children().eq(4).attr("content")

            results.push({
                "productImageUrl": productImageUrl,
                "detailUrl": detailUrl,
                "title": title,
                "description": description
            })
        });
    })
    .then(($) => {
        console.log("SECOND THEN")
        return Promise.all(results.map( item => {
            console.log("SECOND THEN INNER")
            const options1 = {
                uri: item.detailUrl,
                normalizeWhitespace: true,
                transform: function (body) {
                    return cheerio.load(body);
                }
            };

            rp(options1)
                .then(($) => {
                    console.log("SECOND THEN INSIDE 'rp(options1)'")
                    let temp = $('#prod-title').text()
                    let unit = temp.substring(temp.indexOf('Size: ') + 6, temp.indexOf('mL') - 1)
                    let retail = temp.substring(temp.indexOf('Retail: $') + 9, temp.indexOf(' A'))
                    let wholesale = temp.substring(temp.indexOf('Wholesale: $') + 12, temp.indexOf(' A') + 21)
                    results2.push({
                        "productImageUrl": item.productImageUrl,
                        "detailUrl": item.detailUrl,
                        "title": item.title,
                        "description": item.description,
                        "unit": unit,
                        "retail": retail,
                        "wholesale": wholesale
                    })
                })
                .catch((err) => {
                    console.log(err);
                });
        }))
    })
    .finally(($) => {
        console.log("FINALLY " + results2)
        let header = "Handle,Title,Body" + os.EOL

        fs.writeFile("./file.csv", header, function (err) {
            if (err) {
                return console.log(err);
            }
        });

        for (let item of results2) {
            console.log(2)
            let hyphenateTitle = item.title.replace(/\s+/g, '-').toLowerCase();
            let line = hyphenateTitle + "," + item.title + "," + item.description + "," + vendor + ',"","",true,Title,Default Title,,,,,SKU,10000,,1,deny,manual,' + item.retail + "," + item.wholesale + "," + 'true,true,"",' + item.productImageUrl + "," + ',1,,false,,,,,,,,,,,,,,,,,kg,' + os.EOL
            fs.appendFile("./file.csv", line, function (err) {
                if (err) {
                    return console.log(err);
                } else {
                    // done
                }
            })
        }
    })

    .catch((err) => {
        console.log(err);
    });

这是输出:

FIRST THEN
SECOND THEN
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
SECOND THEN INNER
FINALLY 
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'
SECOND THEN INSIDE 'rp(options1)'

1 个答案:

答案 0 :(得分:1)

在第二个then中,您正在运行异步迭代,没有任何内容告诉包装好的Promise等待循环中的Promises(在forEach中)。

您应该return内部的Promise链通知父级Promise等待,并且由于您需要等待迭代中的所有Promise,因此可以使用Promise.all

.then( () => {
   return Promise.all(results.map( item => {
       //...
       return rp(options1).then(($) => {
            //..
        });
    }));
})

根据您的需要,您可以在本地catch(在内部Promise上)或在父Promise上。

请查看Promise documention的详细信息,请参阅common mistakes with Promises部分