Тогда () завершение перед обещанием - PullRequest
0 голосов
/ 02 октября 2018

У меня возникла некоторая основная проблема с 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 голосов
/ 02 октября 2018

Во 2-й then вы выполняете асинхронную итерацию , ничто не говорит оберточному обещанию ждать обещания внутри цикла (в forEach).

Вы должныreturn внутренняя цепочка Promise для информирования родительского Promise об ожидании, и, поскольку вам нужно дождаться всех Promises в вашей итерации, вы можете использовать Promise.all.

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

И вы можете либо catch локально (на внутреннем Promise) или на родительском Promise в зависимости от ваших потребностей.

Пожалуйста, ознакомьтесь с документом Promise для получения подробной информации, см. Раздел Распространенные ошибки, связанные сОбещания

...