【问题标题】:Then() completing before PromiseThen() 在 Promise 之前完成
【发布时间】:2019-03-07 13:27:08
【问题描述】:

我在这里遇到了一些关于 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)'

【问题讨论】:

  • 最后一个 then 在第二个之内,而不是在它之后,因此它将被执行,而(中间)执行给出你得到的结果

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


【解决方案1】:

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

你应该 return 内部 Promise 链通知父 Promise 等待,由于你需要等待迭代中的所有 Promise,你可以使用 Promise.all。

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

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

详情请看Promise documention,见common mistakes with Promises部分

【讨论】:

  • 嗨@krampstudio。在这里感谢您的帮助!实际上,就像您提到的那样,我确实尝试在第二个 then() 中返回承诺,但这不起作用。我认为,我的 Promises 和“rp(options1).then(($)”这一行中的“promise-request”库基本上无法正常工作。我已经通过更改您建议的代码更改了帖子,并且输出。请看一下
猜你喜欢
  • 2016-05-13
  • 2019-02-22
  • 2018-04-05
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
  • 1970-01-01
  • 2019-09-08
  • 2019-02-27
相关资源
最近更新 更多