承诺的问题更多

时间:2014-03-08 19:40:34

标签: javascript promise q

在我之前的问题中,我认为我已将其排序,但我发现了一个间歇性的边缘条件,其中"然后"部分是在所有承诺在Q.all电话中解决之前执行的。

简单地说,我有以下设置,其中使用不同的参数多次调用泛型计算:(代码简化了一点以删除不相关的代码和实际的参数名称)

    var promiseArray = [];
    promiseArray.push(genericCalc(param1, param2, param3));
    promiseArray.push(genericCalc(param1, param2, param3));
    promiseArray.push(genericCalc(param1, param2, param3));

    var calcPromise = Q.all(promiseArray);

    return calcPromise
            .then(calcsDone)
            .fail(calcsFailed);

    function calcsDone(res) {
        calcTotals(); 
        setChart(selectedRow());
        console.log("done recalculation routine");
    }

    function calcsFailed() {
        logger.logError("Failure in Calculations", "", null, true);
    }

genericCalc(删除了一些不相关的代码)

var genericCalc = function (param1, param2, param3) {

    //specific calculation is determined using parameters passed in and varies but is same structure for each as below
    calcPromise = specificCalc(params...);

    return calcPromise
       .then(calcsDone)
       .fail(calcsFailed);

    function calcsDone(res) {  
      //some minor subtotalling in here using "res" and flag setting             
      return Q.resolve();
    }

    function calcsFailed() {
       //handle error....
    }
};

有3或4种不同的特定计算,它们都具有大致相同的布局:

function specificCalc1(params...) {

    var promise = calcContext.ajaxCalc(params);

    return promise
            .then(calcsDone)
            .fail(calcsFailed);

    function calcsDone(res) {
        rv(res);
        console.log("done specific calc 1");
        return Q.resolve();
    }
    function calcsFailed() {
        logger.logError("Failure in specific calc 1", "", null, true);
        return Q.resolve();
    }
};

现在我知道将ajax calc的结果推送到返回值并不是一个好主意,但是目前我不想改变它,因为涉及的代码更改太多而且我我觉得即使不是目前最好的方法论,这对我来说也是一个学习曲线,一旦我绕过这个奇怪的边缘条件,我就会解决这个问题。

所以...让我感到震惊的是,当我在屏幕上更改某些触发重新计算的值时,我会不时地做出这样的事情,其中​​之一就是"完成"来自其中一个特定计算的控制台日志消息在“完成重新计算例程”之后出现。一节来自第一节!

我认为这是由于一个构造不良的承诺导致函数被立即执行,但真正奇怪的是,当我在服务器代码中进行调试停止以进行该计算时,一切正常并且客户端浏览器暂停,直到服务器代码继续,然后客户端调试显示下一个点被命中。

10次中有9次完全正常。在10号,我看到"做了重新计算例程"在控制台中紧接着"完成特定的计算2"这会导致总数出错。

我看不出这是怎么回事。我在所有特定的calc函数中人为地设置时间延迟循环以使它们花费几秒钟而且从来没有一次看到无序的调试消息,但是当没有人为的减速时,我看到它在1约10倍。

请有人试着让我摆脱困境......我只是想知道我可以依靠" Q.all"在第一个代码块中调用工作,这样当它遇到" calcsDone"它总是完成通用和特定的计算!

编辑1: 解释" rv"或者返回一点。在" genericCalc"中,我声明了一个可观察的:

    var res = ko.observable(); //holds returned results

作为对#34; specificCalc"的调用的一部分。我传递了返回值,例如:

calcPromise = specificCalc1(isPackaging, res, thisArticle);

在specificCalc中,我只是将ajax结果放入observable中,因此它可以在" calcsDone" in" genericCalc"并且,一旦所有计算结果都从" Q.all()"函数,计算例程可以将每个特定Calc的各个结果汇总起来。

编辑2 控制台日志出错时是:

done specificCalc1 
done specificCalc2, isPackaging=true 
Done specificCalc3, redraw = false 
done calctotals 
chart replotted 
done recalculation routine
done specificCalc2, isPackaging=false  

...你可以看到"完成了具体的Calc2"在"完成重新计算程序"那里。

编辑3

我将promiseArray减少到只有一个要检查的项目,使用参数来查看麻烦的调用(specificCalc2):

var promiseArray = [];
promiseArray.push(genericCalc(param1, param2, param3));

然后我在服务器代码中加了一个停止点。

结果是服务器代码中的停止发生了,控制台已经说完了#34;已完成"所以它毕竟是承诺构建的一个问题,并且某种方式被其他一个调用所掩盖。一旦我在服务器代码中释放停止,我从ajax调用函数和通用Calc函数得到我的控制台消息,说"完成"。

所以,在我看来好像问题出现在我可以告诉的主要Q.all调用中,因为它不会等待通用calc函数在继续使用&#之前完成34; calcsDone"功能

我刚刚尝试返回genericCalc承诺:

return genericCalc("eol_", true, false, false)
//return calcPromise
        .then(calcsDone)
        .fail(calcsFailed);

...它立刻失败了"无法调用方法"然后"未定义的,因此确认我的问题是在通用计算函数中,所以现在看看它。

编辑4

将问题缩小到genericCalc中的另一个函数调用。作为计算的一部分,这将调用一个函数来在计算完成之前删除影响值。当计算返回时,然后将结果重新添加到总量中。

如果我"返回Q.resolve()"来自genericCalc之前就行了:

impactRemove(currentPrefix, currentArticle); //remove impacts for this prefix

那么承诺是有效的,如果我在线上做了,那就失败了。因此,出于某种原因,调用该函数似乎可以解决这个问题。现在进一步调查。

编辑5 因此,当我在genericCalc例程的中途调用标准函数时会导致问题。这背后的逻辑是:

  1. 更改浏览器表单重新触发计算
  2. 调用函数来设置promises数组(其中几个使用不同的参数调用相同的函数)
  3. 在通用函数(genericCalc)中,我调用标准的非承诺函数,从项目总计中删除当前的计算总数
  4. 计算完成
  5. 标准的非承诺函数,用于将计算结果添加回项目总计
  6. GenericCalc返回对main函数的承诺
  7. 使用最新项目总计更新总体总数,图片已更新
  8. 实际上似乎发生了当我用genericCalc调用标准javascript函数时,它们立即执行,因此解析了promise,虽然ajax调用仍然完成,但Q.all调用不会等待,因为它认为是promise被解决为" genericCalc"正在回归"未定义"而不是承诺。

    此时,Bergi正在尖叫我关于我可怕的反模式noob编码,我倾向于同意他。麻烦的是,我想让它以这种方式工作,所以当我最终使其适应工作时,我有一些东西需要测试。

    所以......如果我有两个从内部调用的函数" genericCalc"像这样:

    impactRemove(currentPrefix, currentArticle); //remove impacts for this prefix
    

    impactAdd(currentPrefix, currentArticle); //addimpacts for this prefix
    

    基本上是这样的:

    function impactAdd(prefix, prjArt) {
        if (!prjArt) {return} //just get out as there's nothing to calculate
        factorArray.forEach(function (f) {
            var orig_projValue = pGlobalVar.editProject()[prefix + f]();
            var new_projArtValue = prjArt[prefix + f](); //must be set first!
            pGlobalVar.editProject()[prefix + f](orig_projValue + new_projArtValue); //add new value back in to project value
        });
    }
    

    ...然后我怎么称呼这些" midpoint"在genericCalc的承诺范围内的函数,这样我仍然可以在a)impactRemove完成时返回一个promise,b)远程ajax调用已经完成,c)impactAdd函数是否已经完成?

    我是否需要在genericCalc中设置代码来执行以下操作:

    impactRemove(params...).then(<ajax function that returns new calculation results>).then(impactAdd(params))
    
    在我的函数自动调用这些函数之后

    ...或将(params),从而过早地解决了这个承诺?与我以前相比,这一切都让人感到困惑!

    Edit6 所有genericCalc都是这样的:

    var genericCalc = function (param1, param2, param3) {
    
        //specific calculation is determined using parameters passed in and varies but is same structure for each as below
        calcPromise = specificCalc(params...);
    
        impactRemove(currentPrefix, currentArticle); //remove impacts for this prefix
    
        return calcPromise
           .then(calcsDone)
           .fail(calcsFailed);
    
        function calcsDone(res) {  
          //some minor subtotalling in here using "res" and flag setting    
    
          impactAdd(currentPrefix, currentArticle); //addimpacts for this prefix
          return Q.resolve();
        }
    
        function calcsFailed() {
           //handle error....
        }
    };
    

    &#34; specificCalc&#34;返回一个承诺 - 当我在断点处检查了承诺的内容时,一个承诺起作用。如果我删除了对&#34; impactRemove&#34;和&#34; impactAdd&#34;上面,那么&#34; genericCalc&#34;也有效。正是那些引起问题的电话。

    这就是为什么我认为我需要这样的东西:

    impactRemove(params)
        .then(return calcPromise(params)
        .then(impactAdd(params);
    

    ...但是影响添加和影响都不会异步地做任何事情我也不确定如何设置它,因为我需要使用params然而你说这些参数意味着函数会被立即调用..

    编辑7

    因此,正如冗长的评论部分所述,这是由一个&#34; forEach&#34;循环使用genericCalc:

    var genericCalc = function (calcPrefix, other params...) {
        gcCount++;
        console.log("generic calc starting for: " + calcPrefix + ", count=" + gcCount);
        pGlobalVar.projectIsCalculating(true); //controls spinner gif
    
        var res_Array = ko.observable(); //holds returned results
        var _prjArticleArray = []; //create local array to use
    
        if (currentArticle == true) {
            _prjArticleArray.push(pGlobalVar.editProjectArticle());
        } else {
            projectResults().projectArticles().forEach(function (pa) {
                _prjArticleArray.push(pa);
            });
        };
    
        _prjArticleArray.forEach(function (thisArticle) {
    
            var calcPromise;
    
            switch (calcPrefix) {
                case "eol_":
                    calcPromise = Q.all([calcEOL(isPackaging, res_Array, thisArticle)]);
                    break;
                case "use_":
                    calcPromise = Q.all([calcUse(isPackaging, res_Array, thisArticle)]);
                    break;
                case "air_":
                    calcPromise = Q.all([calcFlight(isPackaging, res_Array, thisArticle)]);
                    break;
                default:
                    break;
            }
    
            impactRemove(calcPrefix, thisArticle); //remove impacts for this prefix
    
            return calcPromise
                .then(calcsDone)
                .fail(calcsFailed);
    
            function calcsDone(args) {
    
                //do some calcs and totalling based on returned results
    
                impactAdd(calcPrefix, thisArticle); //add this article impact into project total
    
                console.log("generic calc done for: " + calcPrefix + ", count=" + gcCount);
    
                calcTotals(); //accmulates individual results into the "total_xxx" used on the results table and in the chart
                setChart(selectedRow());
    
                pGlobalVar.projectIsCalculating(false);
    
            }
            function calcsFailed() {
                logger.logError("Failure in " + calcPrefix + "calculation", "", null, true);
                impactAdd(calcPrefix); //will add back in results as they were at start of calc
                pGlobalVar.projectIsCalculating(false);
            }
    
        });
    };
    

    我发布它的所有丑陋荣耀的唯一原因是指出,如果我删除&#34; forEach&#34;循环,只为一篇文章运行。为什么forEach循环会让它死于可怕的死亡?

1 个答案:

答案 0 :(得分:1)

我认为您只想交换specificCalc()impactRemove()来电的顺序。即使第一个是异步的,它也会立即开始执行任务 - 只有结果才会在下一个回合中到达。如果你想在同步任务之后链接任何东西,只需将它放在下一行(“分号monad”)。

此外,如果impactRemove确实分配给您的global (!)变量calcPromise,那么它可能不再是一个承诺,并在调用.then()方法时抛出。你想要的似乎是

function genericCalc(param1, param2, param3) {
    impactRemove(currentPrefix, currentArticle); //remove impacts for this prefix
    return specificCalc(params...).finally(function() {
        impactAdd(currentPrefix, currentArticle); // add impacts for this prefix
    }).then(function calcsDone(res) {  
        // some minor subtotalling in here using "res" and flag setting
        return subtotal;
    }, function calcsFailed() {
        // handle error....
    });
}

  

为什么forEach循环会让它死于可怕的死亡?

因为你没有回复承诺。 forEach循环有自己的回调,您可以从return进行回调,但从genericCalc函数返回任何内容。 Q.all不会为此烦恼,只需将undefined作为值即可。但是,异步操作将启动并且您获得回调,但Q.all将不会等待它,因为它不知道它。

解决方案是一个非常简单的变化,已经解释过here