簡體   English   中英

遍歷Promise節點js

[英]iterating over promises node js

嗨,我有一個問題,我試圖遍歷鍵列表,創建匹配實體列表,代碼看起來像這樣

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循環,例如,它什么都不返回?

我也有一些調試

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

請讓我知道是否需要更多信息。 謝謝

下次,如果您實際上用言語(連同代碼)解釋了您要完成的任務,而不僅僅是在問題中轉儲一堆代碼,那么將更容易提供幫助。 我試圖對代碼的目標進行逆向工程,這就是我想您正在嘗試做的事情:

  1. 您在response.keys有一個值數組。
  2. 您想要累積這些鍵的第一個limit匹配數,一旦有limit匹配,就停止尋找更多匹配項。
  3. 您想按response.keys中的項目順序查找那些匹配項。
  4. 使用readEntity(val).then() matchInstance().then()測試檢測匹配。
  5. 所需的結果是匹配條目的數組,其長度不超過limit long。
  6. 當過程中任何地方發生錯誤時,您都想中止並報告錯誤。

這是三個獨立的設計模式,用於序列化數組的迭代,在該模式中,您要對每個數組元素執行異步操作,並希望一個接一個地進行操作(不是並行執行)。 由於要求在收集limit結果后停止處理,因此使您的特殊情況稍微復雜一些。

.reduce()承諾鏈

序列化基於promise的異步操作的經典設計模式是使用.reduce() ,其中約簡中的累加器值是一個promise,您可以鏈接到該promise來強制序列化。 您可以這樣實現:

// 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()回調的.reduce()值始終是相同的entities數組。 這樣,它就沿着鏈向下傳遞,其中鏈中的每個鏈接都有機會添加到鏈中(或者不基於isMatch測試)。


使用next()函數進行手動迭代和鏈接

這是依次迭代的另一種設計模式。 我通常會調用next()一個內部函數。 該函數返回一個Promise或一個值。 如果返回值,則迭代完成。 如果返回承諾,則迭代將繼續,每次迭代都鏈接到前一個。 最終的解析值是您在迭代過程中累積的任何值。 在這里是entities數組。

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()在Series中運行事物

藍鳥承諾庫具有許多有用的功能,用於管理和排序承諾。 其中之一是Promise.mapSeries() ,它將數組作為輸入並序列化對數組中每個項目的調用函數。 因為您要求在達到limit結果后停止,所以我們必須使用與平時稍有不同的方法,但是它仍然使代碼相當簡單:

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
});

關於原始代碼的一些觀察結果:

  1. 您正在使用Promise構造函數反模式 ,其中將其他Promise包裹在手動創建的Promise中 ,而不僅僅是返回您已經擁有的Promise
  2. .then()處理程序中運行新的異步操作時,必須從.then()回調中返回那些承諾,以便將它們鏈接到父承諾。 否則,您將創建各種獨立的未鏈接的承諾鏈,並且您永遠都不知道什么時候完成,也無法協調不同的承諾鏈。
  3. 您的代碼將大致相同的邏輯復制到兩個位置,這從來都不是一個好主意。
  4. 您無法在異步操作中操作for循環索引。 for循環是同步的,並且將在您執行任何異步回調之前完成很長時間。 因此,無論何時嘗試操縱它的索引, for循環都已經完成。

您可以為此編寫一個遞歸函數(不推薦)

let index = 0;
let errors = [];
function promiseIterator (index) {
    if (response.keys[index]) {
      readEntity(response.keys[index]).then((instance) => {
         matchInstance(instance, conditions).then(() => {
           logger.debug("is match: " + isMatch).then(() => {
             if (isMatch) entities.push(instance);
             promiseIterator(index+1);
           });
         });
      }).catch((err) => {
        //error handler
        errors.push({keyIndex: index, err: err});
        promiseIterator(index+1);
      });
    }
}

promiseIterator(offset);
// do your stuff with error array

上面的代碼可能不太准確,請根據您的要求進行調整。

但是由於您需要遍歷Promise,因此建議使用bluebird庫並根據您的要求實現eachall功能。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM