javascript - then() 在 Promise 之前完成

标签 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 上,想法是迭代数组中的每个项目并转到第二页,提取更多信息并将其推送到 结果

最后,在 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你正在运行一个异步迭代,没有任何东西告诉包装 Promise 等待循环内的 Promise(在 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部分

关于javascript - then() 在 Promise 之前完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52608004/

相关文章:

javascript - 如何将 Kotlin 编译成 TypeScript?

javascript - 在 node.js 中共享对象并避免全局变量

javascript - 在 JavaScript 中转置二维数组

java - 异步调用两个 Java 方法

javascript - 如何清除ember js中的表单数据

javascript - Node.js 应用给出 ERR_EMPTY_RESPONSE

javascript - Heroku 和 React 部署成功,但出现应用程序错误

node.js - 在 typescript 中导出模块

c# - 带 TLS/SSL 的异步套接字

javascript - 如何嵌套 jquery.when 调用