【问题标题】:Javascript Promises: Iterate over all object keys arrays and then resolveJavascript Promises:遍历所有对象键数组,然后解析
【发布时间】:2016-11-22 01:49:48
【问题描述】:

我有这个 JS 对象:

let setOfWords = {
    "nouns": [
        "work",
        "construction",
        "industry"
    ],
    "verbs": [
        "work"
    ],
}

我正在使用调用 REST 资源的 google translate API,因此我需要等待每个翻译的响应,然后解析相同的对象结构,但使用翻译后的单词。

function translateByCategory(){
    let translatedObj = {};
    return new Promise(function(resolve, reject){

        Object.keys(obj).forEach(function(category){
            if (translatedObj[category] == undefined) {
                translatedObj[category] = [];
            }
            setOfWords.forEach(function(word){
                google.translate(word, 'es', 'en').then(function(translation){
                    translatedObj[category].push(translation.translatedText);
                });
            });

            // return the translatedObj until all of the categories are translated
            resolve(translatedObj);
        });
    });
}

【问题讨论】:

  • 创建一个promise数组并使用Promise.all(两次,即每个循环)。不要使用new Promise
  • @Bergi 你能发布一个代码示例吗?还是看不懂Promise.all()
  • 顺便说一句,您确定要push word 而不是translation

标签: javascript promise google-translate


【解决方案1】:

您可以使用Promise.all() 等待所有承诺履行(或第一次拒绝):

var translateRequests = [];

Object.keys(setOfWords).forEach(function(category){
        setOfWords[category].forEach(function(word){
            translateRequests.push(google.translate(word, 'es', 'en'));
        });
    });
});

Promise.all(translateRequests).then(function(translateResults){
    //do something with all the results
});

查看文档:Promise.all()

【讨论】:

  • 最好使用map 而不是forEach 来构造数组。虽然我们显然很想念concatMap
  • 考虑到有嵌套的对象被迭代,你会如何干净利落地做到这一点?
  • 谢谢你,它做了一些修改,但现在我明白了Promise.all(),@Bergi 我仍然返回了一个承诺,但在孩子们承诺完成之后。
【解决方案2】:

个人承诺需要与Promise.all() 聚合,从根本上说,这就是缺少的。

但您可以通过减少对 Google 服务的调用次数来做得更好。

Google 翻译 API 允许通过传递一组单词而不是每次调用一个单词来一次性翻译多个文本字符串,从而为您提供性能优势,但可能没有价格优势 - 谷歌目前对其翻译服务收费“按字符”,而不是“每次通话”。

我找不到google.translate() 的任何文档,但是,根据一些假设,您可以写:

function translateByCategory(obj, sourceCode, targetCode) {
    let translatedObj = {};
    var promises = Object.keys(obj).map(function(key) {
        return google.translate(obj[key], sourceCode, targetCode).then(function(translations) {
            translatedObj[key] = translations.map(function(t) {
                return t.translatedText || '-';
            });
        }, function(error) {
            translatedObj[key] = [];
        });
    });
    return Promise.all(promises).then(function() {
        return translatedObj;
    });
}

如果这不起作用,那么this documentation 会解释如何直接调用 google 的 RESTful 翻译服务。

你应该会写:

function translateTexts(baseParams, arrayOfStrings) {
    let queryString = baseParams.concat(arrayOfStrings.map(function(str) {
        return 'q=' + encodeURIComponent(str);
    })).join('&');

    return http.ajax({ // some arbitrary HTTP lib that GETs by default.
        url: 'https://translation.googleapis.com/language/translate/v2?' + queryString,
    }).then(function(response) {
        return response.data.translations.map(function(t) {
            return t.translatedText || '-';
        });
    }, function(error) {
        translatedObj[key] = []; // on error, default to empty array 
    });
}

function translateByCategory(obj, sourceCode, targetCode) {
    let baseParams = [
        'key=' + MY_API_KEY, // from some outer scope
        'source=' + sourceCode, // eg 'en'
        'target=' + targetCode // eg 'es'
    ];
    let translatedObj = {};
    let promises = Object.keys(obj).map(function(key) {
        return translateTexts(baseParams, obj[key]).then(function(translations) {
            translatedObj[key] = translations;
        }, function(error) {
            translatedObj[key] = []; // on error, default to empty array 
        });
    });
    return Promise.all(promises).then(function() {
        return translatedObj;
    });
}

无论哪种情况,调用如下:

let setOfWords = {
    "nouns": [
        "work",
        "construction",
        "industry"
    ],
    "verbs": [
        "work"
    ],
};

translateByCategory(setOfWords, 'en', 'es').then(function(setOfTranslatedWords) {
    console.log(setOfTranslatedWords);
});

【讨论】:

    【解决方案3】:

    @hackerrdave 建议的方法可以修改,使其更兼容 JavaScript 的 async await 特性,如下:

    function translateByCategory(){
    
        return new Promise(function(resolve, reject){
            var translateRequests = [];
            Object.keys(setOfWords).forEach(function(category){
                    setOfWords[category].forEach(function(word){
                        translateRequests.push(google.translate(word, 'es', 'en'));
                });
            });
            Promise.all(translateRequests).resolve(resolve(translateRequests));
        });
    }
    

    所以现在你可以这样做:

    let translatedObj = await translateByCategory();
    

    你会在“translatedObj”中得到你想要的。

    【讨论】:

    • 谢谢。我一直在阅读有关asyncawait 的信息。它不应该取代 Promises 并且与 async 一起工作吗?不过我还不是很明白。
    • 不,asyncawait 使用 Promises,看看 here
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多