迭代promises节点js [英] iterating over promises node js

查看:79
本文介绍了迭代promises节点js的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嗨我有一个问题,我正在尝试迭代一个键列表,创建一个匹配实体列表
代码看起来像这样

Hi i have a problem where i'm trying to iterate over a list of keys, creating a list of matching entities The code looks like this

let previous = undefined;
for (let i = offset; i < response.keys.length; i++) {
    logger.debug("iteration " + i + " previous: " + previous)
    logger.debug("instance key: " + response.keys[i])
    if (previous) {
        previous = previous
            .then((entities) => {
                if (entities.length === limit) {
                    i = response.keys.length;
                } else {
                    readEntity(response.keys[i])
                        .then((instance) => {
                            matchInstance(instance, conditions)
                            logger.debug("is match: " + isMatch)
                                .then((isMatch) => {
                                    if (isMatch)
                                        entities.push(instance);
                                    //resolve(entities);
                                }).catch((err) => {
                                reject(err)
                            })
                        }).catch((err) => {
                        reject(err)
                    })
                }
            }).catch((err) => {
                reject(err)
            })
    } else {
        previous = readEntity(response.keys[i])
            .then((instance) => {
                logger.debug("reading instance: " + instance.key)
                matchInstance(instance, conditions)
                    .then((isMatch) => {
                        if (isMatch) {
                            return instance
                        } else {
                            logger.debug("instance does not match")
                        }
                    }).catch((err) => {
                    reject(err)
                })
            }).catch((err) => {
                reject(err)
            })
    }
}

但它只通过for l oop一次,就像,它没有返回任何东西?

but it only goes through the for loop once, like, it isn't returning anything?

我有一些调试以及

2017-10-04T15:09:58+0200 <debug> data.js:202 (seekKeys.then) iteration 0 previous: undefined
2017-10-04T15:09:58+0200 <debug> data.js:203 (seekKeys.then) instance key: employees/existing@...
2017-10-04T15:09:58+0200 <debug> data.js:202 (seekKeys.then) iteration 1 previous: [object Promise]
2017-10-04T15:09:58+0200 <debug> data.js:203 (seekKeys.then) instance key: employees/test@...
2017-10-04T15:09:58+0200 <debug> data.js:202 (seekKeys.then) iteration 2 previous: [object Promise]
2017-10-04T15:09:58+0200 <debug> data.js:203 (seekKeys.then) instance key: employees/unique@...
2017-10-04T15:09:58+0200 <debug> data.js:202 (seekKeys.then) iteration 3 previous: [object Promise]
2017-10-04T15:09:58+0200 <debug> data.js:203 (seekKeys.then) instance key: employees/update@...
2017-10-04T15:09:59+0200 <debug> data.js:231 (readEntity.then) reading instance: existing@...
2017-10-04T15:09:59+0200 <debug> data.js:237 (matchInstance.then) instance does not match

如果您需要,请告诉我更多信息。谢谢

please let me know if you need more info. Thank you

推荐答案

下一次,如果不是只是在一个问题中转储一堆代码,那么它会更容易帮助你实际上解释了你想要用语言完成的东西(以及代码)。我试图对代码的目标进行逆向工程,这就是我认为你想要做的事情:

Next time it would be easier to help if, rather than just dumping a bunch of code in a question, you actually explained what you are trying to accomplish in words (along with the code). I tried to reverse engineer the objective of the code and this is what I think you were trying to do:


  1. 你有一个值数组在 response.keys

  2. 您想积累第一个限制号码一旦你有限制匹配,这些键的匹配并停止寻找更多匹配。

  3. 你想按顺序寻找那些匹配 response.keys 中的项目。

  4. 使用 readEntity(val)检测匹配.then() 后跟一个 matchInstance()。然后() test。

  5. 所需的结果是一个数组匹配的条目不超过 limit long。

  6. 当进程中的任何位置发生错误时,您希望中​​止并报告错误。

  1. You have an array of values in response.keys.
  2. You want to accumulate the first limit number of matches of those keys and stop looking for more matches once you have limit matches.
  3. You want to look for those matches in order of the items in response.keys.
  4. Matches are detected with readEntity(val).then() followed by a matchInstance().then() test.
  5. The desired result is an array of matched entries no longer than limit long.
  6. When an error occurs anywhere in the process, you want to abort and report the error.

以下是三种独立的设计模式,用于序列化数组的迭代,您可以在每个数组元素上执行异步操作,并希望这样做他们o一个又一个(不是平行)。在收集 limit 结果后,要求停止处理会使您的特定情况稍微复杂一些。

Here are three separate design patterns for serializing an iteration of an array where you are doing asynchronous operations on each array element and want to do them one after another (not in parallel). Your particular case is made slightly more complicated by the requirement to stop processing after limit results are collected.

.reduce()承诺链接

序列化基于承诺的异步操作的经典设计模式是使用 .reduce( )其中减少中的累加器值是一个承诺,并链接到该承诺以强制序列化。您可以这样实现:

A classic design pattern for serializing promise-based async operations is to use .reduce() where the accumulator value in the reduction is a promise and you chain to that promise to force serialization. You can implement that like this:

// get subset of response.keys that we can process with .reduce()
let keys = response.keys.slice(offset);
keys.reduce((p, val) => {
    // return promise so we continually chain
    return p.then(entities => {
        // if entities is not yet full
        if (entities.length < limit) {
            return readEntity(val).then(instance => {
                return matchInstance(instance, conditions).then(isMatch => {
                    if (isMatch) {
                        entities.push(instance);
                    }
                    // resolved value is entities so that is passed down the chain
                    return entities;
                });
            });
        } else {
            return entities;
        }
    });
}, Promise.resolve([])).then(entities => {
    // process results here
}).catch(err => {
    // process error here
});

示例实施: https://jsfiddle.net/jfriend00/uspa8vgd/

在此承诺链中,承诺的已解决价值是结果数组。它被初始化为一个空数组,其中 Promise.resolve([])传递给 .reduce()作为初始累加器值,然后 .reduce()回调的解析值始终是相同的实体数组。通过这种方式,它在链中传递,链中的每个链接都有机会添加到它(或者不是基于 isMatch 测试)。

In this promise chain, the resolved value of the promise is the array of results. It is initialized to an empty array with the Promise.resolve([]) that is passed to .reduce() as the initial accumulator value and then the resolves value of the .reduce() callback is always that same entities array. In this way, it is passed down the chain where each link in the chain has an opportunity to add to it (or not based on the isMatch test).

手动迭代并使用next()函数链接

这是另一种按顺序迭代的设计模式。我创建了一个内部函数,我通常称之为 next()。该函数要么返回一个promise或一个值。如果它返回一个值,则迭代完成。如果它返回一个promise,则迭代继续,每次迭代链接到前一个迭代。最终解析的值是您在迭代期间累积的任何值。在这里它是实体数组。

Here's another design pattern for iterating in sequence. An internal function which I usually call next() is created. That function either returns a promise or a value. If it returns a value, then the iteration is done. If it returns a promise, then the iteration continues, with each iteration chaining onto the previous one. The eventual resolved value is whatever value you accumulated during the iteration. In here it's the entities array.

function runMatchIteration(data, offset, limit) {
    let i = offset;
    let entities = [];

    function next() {
        if (i < data.length && entities.length < limit) {
            return readEntity(data[i++]).then(instance => {
                return matchInstance(instance, conditions).then(isMatch => {
                    if (isMatch) {
                        entities.push(instance);
                    }
                    // now execute next cycle - chaining onto original promise
                    return next();
                });
            });

        } else {
            // done with loop here
            return entities;
        }
    }

    return Promise.resolve().then(next);
}


// usage
runMatchIteration(response.keys, offset, limit).then(entities => {
    // process results here
}).catch(err => {
    // process error here
});

示例实施: https://jsfiddle.net/jfriend00/t5bmzkb6/

使用Bluebird的Promise.mapSeries()在系列中运行

Bluebird promise library 有许多有用的函数来管理和排序promise。其中包括 Promise.mapSeries() 将数组作为输入并序列化调用数组中每个项的函数。因为你要求在 limit 结果之后停止,我们必须使用它与平时略有不同,但它仍然使代码相当简单:

The Bluebird promise library has a number of helpful functions for managing and sequencing promises. Among those is Promise.mapSeries() which takes an array as input and serializes calling a function on each item in the array. Because your requirement to stop after limit results are achieved, we have to use it slightly different than usual, but it still makes the code fairly simple:

let entities = [];
Promise.mapSeries(response.keys.slice(offset), item => {
    if (entities.length < limit) {
        return readEntity(item).then(instance => {
            return matchInstance(instance, conditions).then(isMatch => {
                if (isMatch) {
                    entities.push(instance);
                }
            });
        });
    }
}).then(() => {
    // process entities result here
}).catch(err => {
    // handle error here
});






对原始代码的一些观察:


Some observations on your original code:


  1. 您正在使用承诺构造函数反模式,其中您将其他承诺包装在手动创建的承诺中,而不是仅仅返回您已有的承诺。

  2. .then()处理程序中运行新的异步操作时,必须从 .then()返回这些promise。 回调,以便它们链接到父承诺。否则,您将创建各种独立的未链接的承诺链,并且您永远不知道何时完成任何事情并且无法协调不同的承诺链。

  3. 您的代码将大致相同的逻辑复制到两个地方这绝不是一个好主意。

  4. 您无法在异步操作中操作 for 循环索引。 循环的是同步的,并且在任何异步回调执行之前已经完成了很长时间。因此,每当您尝试操作其索引时,循环的就已经完成。

  1. You are using the Promise constructor anti-pattern where you wrap other promises inside a manually created promise rather than just return the promise you already have.
  2. When running new async operations inside a .then() handler, you must return those promises from the .then() callback so that they are chained to the parent promise. Otherwise, you create all sorts of independent unlinked promise chains and you never know when anything is finished and can't coordinate the different promise chains.
  3. Your code copies roughly the same logic into two places which is never a good idea.
  4. You can't manipulate a for loop index inside an asynchronous operation. The for loop is synchronous and will have already finished long before any of your async callbacks execute. So, the for loop is already done whenever you try to manipulate its index.

这篇关于迭代promises节点js的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆