首页 > 解决方案 > 在 JS /Node JS 中使用 promise 每次都会遍历每个对象

问题描述

我正在尝试制作一个程序来验证链接是否损坏或工作。我是 JS 的新手,也是 Promise 的新手。我正在测试一个包含一个损坏的链接和一个工作的降价文件,显然,每次我运行代码时,它都会运行两次(每个链接一个,如果我添加 3 个链接,它会运行 3 次)。

这是我收到的输出

结果

这是我的代码

const path = require('path');
const fs = require('fs');
const fetch = require('node-fetch');

const inputPath = process.argv[2];
const inputOptions = process.argv[3];
const inputOptionsTwo = process.argv[4];

let okLinks = [];
let okLinksCount = 0;
let notOkLinks = [];
let notOkLinksCount = 0;


const checkFilePath = () => {
    let pathExt = path.extname(inputPath);
    if (pathExt == '.md') {
        console.log('md file')
        parseFile(inputPath);
    } else {
        console.log('file not recognized');
    }
};




const parseFile = (inputPath) => {
    fs.readFile(inputPath, 'utf8', (err, data) => {
        if (!err) {
            const regex = new RegExp(/(https?:\/\/[^\s\){0}]+)/g);
            const links = data.match(regex);
            if (links) {
                //function to validate, pass the links as parameter
                validateLinks(links);
            } else {
                console.log('no links found');
            }
        } else {
            //error reading files
            console.log('an error ocurred');
            console.error(error.message);
        }
    });
};


const validateLinks = (links) => {

    for (let i = 0; i < links.length; i++) {
        const p = new Promise(resolve => {

            fetch(links[i])
                .then(res => {

                    if (res.status >= 400) {
                        notOkLinksCount++;
                        notOkLinks.push(links[i] + ' FAIL : ' + res.status);
                    } else {
                        okLinks.push(links[i] + ' OK : ' + res.status);
                        okLinksCount++;

                    }
                    console.log('f');

                    if (inputOptions === '--validate') {
                        setTimeout(function() {
                            console.log(notOkLinks);
                            console.log(okLinks);
                        }, 500);
                    } else if (inputOptions === '--stats' && inputOptionsTwo === '--validate') {
                        setTimeout(function() {
                            console.log('Total: ' + links.length + '\n' + 'Ok: ' + okLinksCount);
                            console.log('Broken: ' + notOkLinksCount);
                            console.log(notOkLinks);
                            console.log(okLinks);
                        }, 2800);
                    } else if (inputOptions === '--stats') {
                        setTimeout(function() {
                            console.log('Total: ' + links.length + '\n' + 'Ok: ' + okLinksCount);
                        }, 2800);
                    }
                }).catch((error) => {
                    console.error('Error');
                });


        })
    }

}




checkFilePath();

标签: javascriptnode.jsloopspromise

解决方案


如果您能够在您的环境中使用async/await语法,那么它将使 Promise 的推理更容易。

目前代码不等待异步代码完成,这就是我认为setTimeouts添加的原因。像这样使用它们是不稳定的,因为它们是在猜测代码需要多长时间才能完成,而不是真正等到它完成或出错。

尽量避免将回调混入 Promise 代码fs.readFile中,比如 Nodefs现在提供了一个 Promise API

Bluebird Promise 库提供了一些帮助器和Promise.mapPromise.filter用于处理数组。另外Promise.delay,如果您确实需要setTimeout出于其他原因使用。

结合所有这些意味着对代码进行相当多的重组。

const fsp = require('fs').promises;
const fetch = require('node-fetch');
const Promise = require('bluebird');

const parseFile = async (inputPath) => {
    try {
        const data = await fsp.readFile(inputPath, 'utf8')
        const regex = new RegExp(/(https?:\/\/[^\s\){0}]+)/g);
        const links = data.match(regex);
        if (!links) {
            return console.log('no links found');
        } 
        //function to validate, pass the links as parameter
        return validateLinks(links);
    } catch (error) {
        //error reading files
        console.error('An error occurred processing '+inputPath);
        throw error
    }
};

const checkLink = async (link) => {
    try {
        const res = await fetch(link)
        if (res.status >= 400) {
            const error = new Error(`'Request failed for ${link} with ${res.status}`)
            error.res = res
            error.status = status
            throw error
        }
        return link;
    }
    catch (error) {
        error.link = link
        throw error
    }
}

const validateLinks = async (links) => {
    const notOkLinks = [];
    const okLinks = await Promise.filter(links, async link => {
        try {
            return checkLink(link)
        }
        catch (error) {
            console.error('Error for %s', link, error);
            notOkLinks.push(error)
            return false
        }
    })

    return {
        okLinks,
        notOkLinks,
    }
}

然后您可以重新实现checkFilePathtoawait parseFile()并处理对象中返回的okLinksand的日志记录。notOkLinks删除所有setTimeouts ,因为awaitwill 在继续之前等待承诺完成。


推荐阅读