【问题标题】:forEach loop behaving strangely with called function values logged at end of the loop instead of duringforEach 循环的行为很奇怪,调用函数值记录在循环结束而不是在循环期间
【发布时间】:2021-02-06 01:43:40
【问题描述】:

编辑: 目前我认为问题在于 forEach 不知道承诺。 https://zellwk.com/blog/async-await-in-loops/

我正在尝试应用一个节点 javascript 翻译函数(我把它放在帖子的末尾,因为它很长)来循环一个值数组。但是,当我出于某种原因循环时,我只会在循环完成后出现循环函数的某些部分:请允许我说得更清楚:

array = [["hello", "hello" ],
         ["my", "my",  ],
         ["name", "name" ],
         ["is", "my" ],
         ["joe", "joe"]]

function process (item,index){
   
const translate = require('@vitalets/google-translate-api');
      
      console.log('loopaction'); //this shows that the loop is executing
    translate(item[0], {to: 'sp'}).then(result => {
    console.log(result.text);
  
}).catch(err => {
    console.error(err);
})


array.forEach(process);   // Applying the function process to the array in a ForEach loop

我从中得到了

循环动作 循环动作 循环动作 循环动作 循环动作

你好 米 名词 es 乔

所以似乎 forEach 循环在允许显示值之前完成。这是我真的不明白的,因为数组值被正确翻译,然后以正确的顺序注销。就好像它们被存储在记忆中以备后用。然后在 forEach 循环顺序结束时调用。

翻译函数如下所示:

function translate(text, opts, gotopts) {
opts = opts || {};
gotopts = gotopts || {};
var e;
[opts.from, opts.to].forEach(function (lang) {
    if (lang && !languages.isSupported(lang)) {
        e = new Error();
        e.code = 400;
        e.message = 'The language \'' + lang + '\' is not supported';
    }
});
if (e) {
    return new Promise(function (resolve, reject) {
        reject(e);
    });
}

opts.from = opts.from || 'auto';
opts.to = opts.to || 'en';
opts.tld = opts.tld || 'com';

opts.from = languages.getCode(opts.from);
opts.to = languages.getCode(opts.to);

var url = 'https://translate.google.' + opts.tld;
return got(url, gotopts).then(function (res) {
    var data = {
        'rpcids': 'MkEWBc',
        'f.sid': extract('FdrFJe', res),
        'bl': extract('cfb2h', res),
        'hl': 'en-US',
        'soc-app': 1,
        'soc-platform': 1,
        'soc-device': 1,
        '_reqid': Math.floor(1000 + (Math.random() * 9000)),
        'rt': 'c'
    };

    return data;
}).then(function (data) {
    url = url + '/_/TranslateWebserverUi/data/batchexecute?' + querystring.stringify(data);
    gotopts.body = 'f.req=' + encodeURIComponent(JSON.stringify([[['MkEWBc', JSON.stringify([[text, opts.from, opts.to, true], [null]]), null, 'generic']]])) + '&';
    gotopts.headers['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

    return got.post(url, gotopts).then(function (res) {
        var json = res.body.slice(6);
        var length = '';

        var result = {
            text: '',
            pronunciation: '',
            from: {
                language: {
                    didYouMean: false,
                    iso: ''
                },
                text: {
                    autoCorrected: false,
                    value: '',
                    didYouMean: false
                }
            },
            raw: ''
        };

        try {
            length = /^\d+/.exec(json)[0];
            json = JSON.parse(json.slice(length.length, parseInt(length, 10) + length.length));
            json = JSON.parse(json[0][2]);
            result.raw = json;
        } catch (e) {
            return result;
        }

        if (json[1][0][0][5] === undefined) {
            // translation not found, could be a hyperlink?
            result.text = json[1][0][0][0];
        } else {
            json[1][0][0][5].forEach(function (obj) {
                if (obj[0]) {
                    result.text += obj[0];
                }
            });
        }
        result.pronunciation = json[1][0][0][1];

        // From language
        if (json[0] && json[0][1] && json[0][1][1]) {
            result.from.language.didYouMean = true;
            result.from.language.iso = json[0][1][1][0];
        } else if (json[1][3] === 'auto') {
            result.from.language.iso = json[2];
        } else {
            result.from.language.iso = json[1][3];
        }

        // Did you mean & autocorrect
        if (json[0] && json[0][1] && json[0][1][0]) {
            var str = json[0][1][0][0][1];

            str = str.replace(/<b>(<i>)?/g, '[');
            str = str.replace(/(<\/i>)?<\/b>/g, ']');

            result.from.text.value = str;

            if (json[0][1][0][2] === 1) {
                result.from.text.autoCorrected = true;
            } else {
                result.from.text.didYouMean = true;
            }
        }

        return result;
    }).catch(function (err) {
        err.message += `\nUrl: ${url}`;
        if (err.statusCode !== undefined && err.statusCode !== 200) {
            err.code = 'BAD_REQUEST';
        } else {
            err.code = 'BAD_NETWORK';
        }
        throw err;
    });
});
}

我意识到有一种承诺格式,我遇到的问题可能与函数的异步性以及承诺需要多长时间才能得到解决有关。我似乎无法弄清楚为什么在我的 forEach 函数完全循环后承诺没有解决或显示,但它似乎以正确的顺序正确保存。很奇怪。

关于导致这种情况发生的函数 translate() 的任何想法?无论如何我可以重写我的函数 process () 以确保 translate 函数解析 promise 并且函数 process () 中的 .then() 在继续之前完全执行?

【问题讨论】:

    标签: javascript node.js foreach promise


    【解决方案1】:

    你是对的,你正在使用 Promise,所以 translate() 将在你的其余代码执行时异步运行(在后台)。这就是为什么您在 translate 函数返回之前遍历所有 foreach() 并因此得到该输出的原因。

    但是,在异步函数或 Promise 块中使用 forEach 循环也存在问题。不等待回调函数。因此,promise 链被破坏,导致意外行为。 不要在 promise 或 async 函数中使用 forEach 循环。相反,使用 for 循环遍历数组的项目:

    为避免这些问题,请将 forEach 循环更改为 For 循环并像这样使用 asyncawait

    async function process (item,index){
        const translate = require('@vitalets/google-translate-api');
        console.log('loopaction'); //this shows that the loop is executing
        await translate(item[0], {to: 'sp'})
        .then(result => {
            console.log(result.text);
        })
        .catch(err => {
            console.error(err);
        })
    }
    
    async function main() {
        array = [["hello", "hello" ],
             ["my", "my" ],
             ["name", "name" ],
             ["is", "my" ],
             ["joe", "joe"]]
        
        for (let i = 0; i < array.length; i++) {
            await process(array[i], i);
        }
    }
    
    main()
    

    await 使函数等待直到 promise 被解决。

    注意:您尝试使用object.sleep() 创建超时,这在javascript 中不存在,请改用setTimeout(),参考:Sleep() in Javascript

    【讨论】:

    • 感谢您的回复。我投了赞成票,因为这似乎是正确的方向,但不幸的是我得到了相同的结果。
    • 我将编辑问题以包含您的建议。
    • 我已经编辑了答案,试试它是否按预期工作。
    • 可能你会得到一个空的结果,因为你还必须在 translate 函数中添加 async 和 await 。我现在要把它添加到答案中。
    • 谢谢!我运行了您的第一次编辑,但运行方式与以前相同。至于下一个:理想情况下,我希望不编辑翻译功能,尽管如果必须这样做也很好
    猜你喜欢
    • 1970-01-01
    • 2014-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-11
    • 2020-07-12
    • 1970-01-01
    相关资源
    最近更新 更多