获取多个承诺,只返回一个

时间:2017-01-06 14:43:39

标签: javascript asynchronous promise fetch

我有一个我希望获取的网址列表。所有这些url都返回一个带有属性valid的json对象。但是只有一个获取承诺具有valid的神奇true属性。

我尝试了url.forEach(...)Promises.all([urls]).then(...)的各种组合。目前我的设置是:

const urls = [
    'https://testurl.com', 
    'https://anotherurl.com', 
    'https://athirdurl.com' // This is the valid one
];

export function validate(key) {
    var result;
    urls.forEach(function (url) {
        result = fetch(`${url}/${key}/validate`)
            .then((response) => response.json())
            .then((json) => {
                if (json.license.valid) {
                    return json;
                } else {
                   Promise.reject(json);
                }
            });
    });

    return result;
}

由于异步承诺,上述方法无法正常工作。如何在第一个valid == true被击中时迭代我的网址并返回?

4 个答案:

答案 0 :(得分:3)

让我在混合中输入一个很好的紧凑条目

它使用Promise.all,但是在这种情况下,每个内部Promise都会捕获任何错误并简单地解析为false,因此Promise.all永远不会拒绝任何完成但没有许可的提取。有效也将解析为false

进一步处理数组Promise.all resolves,过滤掉false值,并返回第一个(来自问题描述应该是唯一的)有效的JSON响应

const urls = [
    'https://testurl.com', 
    'https://anotherurl.com', 
    'https://athirdurl.com' // This is the valid one
];

export function validate(key) {
    return Promise.all(urls.map(url => 
        fetch(`${url}/${key}/validate`)
        .then(response => response.json())
        .then(json => json.license && json.license.valid && json)
        .catch(error => false)
    ))
    .then(results => results.filter(result => !!result)[0] || Promise.reject('no matches found'));
}

答案 1 :(得分:1)

请注意,validate无法返回结果(see here for why)。但它可以为结果返回 promise

你想要的是类似Promise.race,但不完全相同(Promise.race会拒绝其中一个fetch承诺在另一个承诺之前被拒绝用valid = true解析。因此,当您获得valid为真的第一个分辨率时,只需创建一个承诺并解决它:

export function validate(key) {
    return new Promise((resolve, reject) => {
        let completed = 0;
        const total = urls.length;
        urls.forEach(url => {
            fetch(`${url}/${key}/validate`)
                .then((response) => {
                    const json = response.json();
                    if (json.license.valid) {
                        resolve(json);
                    } else {
                        if (++completed === total) {
                            // None of them had valid = true
                            reject();
                        }
                    }
                })
                .catch(() => {
                    if (++completed === total) {
                        // None of them had valid = true
                        reject();
                    }
                });
        });
    });
}

请注意失败案例的处理。

请注意,如果您愿意,可以将这两个completed检查分解出来:

export function validate(key) {
    return new Promise((resolve, reject) => {
        let completed = 0;
        const total = urls.length;
        urls.forEach(url => {
            fetch(`${url}/${key}/validate`)
                .then((response) => {
                    const json = response.json();
                    if (json.license.valid) {
                        resolve(json);
                    }
                })
                .catch(() => {
                    // Do nothing, converts to a resolution with `undefined`
                })
                .then(() => {
                    // Because of the above, effectively a "finally" (which we
                    // may get on Promises at some point)
                    if (++completed === total) {
                        // None of them had valid = true.
                        // Note that we come here even if we've already
                        // resolved the promise -- but that's okay(ish), a
                        // promise's resolution can't be changed after it's
                        // settled, so this would be a no-op in that case
                        reject();
                    }
                });
        });
    });
}

答案 2 :(得分:0)

可以使用SynJS完成此操作。这是一个有效的例子:

var SynJS = require('synjs');
var fetchUrl = require('fetch').fetchUrl;

function fetch(context,url) {
    console.log('fetching started:', url);
    var result = {};
    fetchUrl(url, function(error, meta, body){
        result.done = true;
        result.body = body;
        result.finalUrl = meta.finalUrl; 
        console.log('fetching finished:', url);
        SynJS.resume(context);
    } );

    return result;
}

function myFetches(modules, urls) {
    for(var i=0; i<urls.length; i++) {
        var res = modules.fetch(_synjsContext, urls[i]);
        SynJS.wait(res.done);
        if(res.finalUrl.indexOf('github')>=0) {
            console.log('found correct one!', urls[i]);
            break;
        }
    }
};

var modules = {
        SynJS:  SynJS,
        fetch:  fetch,
};

const urls = [
              'http://www.google.com', 
              'http://www.yahoo.com', 
              'http://www.github.com', // This is the valid one
              'http://www.wikipedia.com'
          ];

SynJS.run(myFetches,null,modules,urls,function () {
    console.log('done');
});

它会产生以下输出:

fetching started: http://www.google.com
fetching finished: http://www.google.com
fetching started: http://www.yahoo.com
fetching finished: http://www.yahoo.com
fetching started: http://www.github.com
fetching finished: http://www.github.com
found correct one! http://www.github.com
done

答案 3 :(得分:-1)

如果您想避免测试每个网址,可以使用以下代码。

For Each item In fol.Items

    If item.IsFolder Then

        Set fol2 = item.GetFolder

        ProcessFolderRecursively fol2, row

    Else

       Sheets("Sheet2").Select

            Cells(row, 1) = item.path

            row = row + 1  
    End If

Next