【问题标题】:Nested For Loops converted to Nested Promises嵌套的 For 循环转换为嵌套的 Promise
【发布时间】:2017-08-08 17:58:10
【问题描述】:

我遇到了一个问题,我的程序仅在 nameList 的一次迭代中结束,我不确定不合逻辑的代码在哪里。

全局给定:

var _ = require("underscore");
var nameList = ["Bob", "Susie"]
var jsonDict = {}

我的复杂嵌套从这里开始,但我不确定如何修复它,以便它遍历 nameList 和数字 for-loop 1-10

return new Promise((res, rej) => {
    var promises = [];
    return Promise.map(nameList, function(personName){
        for (var j=1; j<=10; j++){
            return promises.push(new Promise(function(resolve, reject) {
                params['page'] = j;

                console.log('PAGE ' + j + ' ' + personName)

                SOME_SEARCH_FX_THAT_RETURNS_A_PROMISE(params).then(function(data){
                    return Promise.map(data, function(item, index){
                        if (!_.has(jsonDict, item.key)){
                            jsonDict[item.key] = {
                                name: personName
                            }
                        }
                        return
                    }).then(function(){
                        console.log(jsonDict)
                        return resolve(true)
                    })

                }).catch(function(err){
                    console.log(err)
                    return reject(false)
                })
            }))
        }
    }).then(function(){
        return res(true)
    })
}).then(function(){
    console.log('Done with everything.')
})

我得到以下输出:

PAGE 1 Bob
PAGE 1 Susie
Done with everything.

{ 
    '12345': { name: "Bob" },
    '12346': { name: "Bob" },
    ...
    '99999': { name: "Bob" }
}

我从来没有得到Susie 的数据,我很早就回来了,但似乎不知道在哪里。任何有关问题所在(甚至重构)的帮助/指导将不胜感激。提前致谢!

【问题讨论】:

  • 您还掉入了在循环变量上形成闭包的常见陷阱,而不是它的值,这意味着您将处理页面n多个次,并忽略所有其他页面

标签: javascript promise bluebird


【解决方案1】:

首先:您的代码存在根本缺陷

我之所以说根本性缺陷是因为你似乎误解了functions 的工作原理

return Promise.map(nameList, function(personName){  <-- this function 
  for (var j=1; j<=10; j++){
    return promises.push(new Promise(function(resolve, reject) { <-- is returning HERE

这里Array.prototype.push函数返回新添加项的index。 (我查过了,直到)
这里最大的问题是你返回它。

只要您return 使用Promise.mapmapper 函数,您就告诉它您完成了!所以在你上面的代码中,它甚至不会进入for loop的下一次迭代

在 MDN 中查找 return 的规范

return 语句结束函数执行并指定要返回给函数调用者的值。

希望这能回答您提早返回的问题。


第二:我不会指望你的承诺

我自己也很难兑现承诺。让我引导你到我最近读到的awesome articles on promises。现在让我应用我从中学到的东西。感谢您提出这个问题。

我们承诺与 callback hell 作斗争,但你却歪曲了它的全部目的。

我将尝试从最嵌套的部分开始扁平化承诺

SOME_SEARCH_FX_THAT_RETURNS_A_PROMISE(params).then(function(data){
    return Promise.map(data, function(item, index){
        if (!_.has(jsonDict, item.key)){
            jsonDict[item.key] = {
                name: personName
            }
        }
        return
    }).then(function(){
        console.log(jsonDict)
        return resolve(true)
    })

}).catch(function(err){
    console.log(err)
    return reject(false)
})

这里的内部承诺映射是不必要的,你可以只使用标准的 map 甚至是 for loop,因为你实际上并没有在这里映射任何东西......

[重构1]

// Create subroutine, careful with the globals...
let populateJsonDict = singleData => {
  if (!_.has(jsonDict, singleData.key)) jsonDict[singleData.key] = { name: personName }
}

SOME_SEARCH_FX_THAT_RETURNS_A_PROMISE(params).then(data => {
    data.forEach(populateJsonDict);
    resolve(true); // this will be removed later
}).catch(function(err){
    console.log(err);
    reject(false); // this will be removed later
})

好的,让我们向上移动回调金字塔(或向下移动?)。列表中的下一个

for (var j=1; j<=10; j++){
  return promises.push(new Promise(function(resolve, reject) {
    params['page'] = j;

    console.log('PAGE ' + j + ' ' + personName)

    //[Refactored 1]
  }))
}

这里又是另一个不必要的 Promise,是时候摆脱它了。而这里问题的基石,return

[重构 2]

for (var j=1; j<=10; j++){
  //[from Refactored 1]
  let populateJsonDict = singleData => {
    if (!_.has(jsonDict, singleData.key)) jsonDict[singleData.key] = { name: personName }
  }
  params['page'] = j; // I assume this a global somewhere
  let p = SOME_SEARCH_FX_THAT_RETURNS_A_PROMISE(params).then(data => {
    data.forEach(populateJsonDict);
    // Removed because we've removed the parent promise
  }).catch(function(err){
    console.log(err);
    // Removed because we've removed the parent promise
  })
  promises.push(p)
}

我会快进,因为我注意到这真的很长。下一个要重构的部分

return new Promise((res, rej) => {
    var promises = [];
    return Promise.map(nameList, function(personName){
      //[Refactored 2]
    }).then(function(){
      return res(true)
    })
}).then(function(){
    console.log('Done with everything.')
})

我真的不知道如何挽救这个,所以我会分组写东西。

[重构 3:final]

var promises = [];
nameList.forEach(personName => { // Like earlier, redundant Promise.map
  //[from Refactored 2]
  for (var j=1; j<=10; j++){
    let populateJsonDict = singleData => {
        if (!_.has(jsonDict, singleData.key)) jsonDict[singleData.key] = { name: personName }
    }
    params['page'] = j;
    let p = SOME_SEARCH_FX_THAT_RETURNS_A_PROMISE(params).then(data => {
        data.forEach(populateJsonDict);
    }).catch(function(err){
        console.log(err);
    })
    promises.push(p)
  }
});
// At this point you have an array of Promises, for this we can utilize Promise.all

Promise.all(promises)
    .then(() => console.log('Done with Everything'));

我想这本来可以做得更好。让我做最后一个版本。

[重构 3.1:final]

let populateJsonDict = name => key => !_.has(jsonDict, key) && Object.assign(jsonDict, {[key]:name};
let promises = _.times(10, 
    index => {
        params['page'] = index+1;
        return Promise.map(nameList, name => {
            let populateWithName = populateJsonDict(name);
            let iterate = data => data.forEach(populateWithName);
            return SOME_SEARCH_FX_THAT_RETURNS_A_PROMISE(params)
                .then(iterate)
                .catch(err => console.log(err));
        });
    });

Promise.all(promises)
    .then(() => console.log('Everything done'));

好吧,仍然有这种不满意的感觉,但这就是我现在所拥有的……这对我来说真的比你更重要。所以再次感谢你的朋友。我希望我们都能够继续推进这个不断变化的工作领域。如果这一切听起来有点居高临下,我深表歉意。干杯!

【讨论】:

  • "承诺与回调地狱作斗争" - 不是真的,它们是为了给我们可返回值,并且能够@ 987654345@ 来自异步回调。我们仍然到处使用回调。为了对抗他们,发明了async/await
  • 感谢您的广泛回答——感谢您的推理,我明白为什么我的代码提前结束了。我仍在尝试围绕这个概念(以及如何更扁平地思考)而思考,并且您链接的文章非常有帮助-尤其是在新手错误部分中看到了我的错误。经过一些调整,我的代码现在可以工作了。
  • 对其他读者/对我也有帮助:stackoverflow.com/questions/354909/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-17
  • 2020-11-26
  • 1970-01-01
  • 2021-12-30
  • 2012-11-06
  • 2021-09-16
  • 1970-01-01
相关资源
最近更新 更多