承诺在for循环中更改全局变量

时间:2016-06-23 16:00:12

标签: javascript node.js callback promise bluebird

我正在尝试对不同风格的javascript代码进行基准测试,这就是我所拥有的:

for (var i = 1; i < 10001; i++) {
    promise(i).then(function(x) {
        h = h + (x*3); if (x===10000) console.log('h:', h);
    }).catch(function(x) {h = h + (x*2);}); // h: 75015000 
}

有意思的是,承诺不会改变全局变量'h',我怎样才能使它返回与变量'g'相同的结果?

更新

以下是尝试获取最终结果的更改代码,但承诺的非确定性会给我们带来意想不到的结果。

for (var i = 1; i < 10001; i++) {
    promise(i).then(function(x) {
        h = h + (x*3);
    }).catch(function(x) {
        h = h + (x*2);
        if (x===9999) console.log('h:', h);  // <- attention here
    }); // h: 125015000
}

目前我提供预期输出的代码甚至更奇怪。

{{1}}

有人能告诉我更好的代码并解释上面的代码吗? (上面的代码显示当i为9999而不是10000时的确定性正确结果)

2 个答案:

答案 0 :(得分:2)

Promises总是异步调用他们的.then().catch()处理程序。即使它们立即得到解决,它们也​​会让JS的当前线程完成执行,并在“下一个勾号”上异步调用.then().catch()个处理程序。

因此,在调用循环中的任何console.log('h:', h);处理程序之前,您的.then()被执行。如果您将console.log()语句放在.then().catch()处理程序中,您会发现它们已被调用,但在您console.log('h:', h);被调用之后。

Promise被设计为异步接口。并且,异步接口始终保持一致非常重要,因此即使同步解析了promise,它们仍会在下一个tick上异步调用它们的.then().catch()处理程序,以便它们始终保持一致并且使用它们的开发人员无需担心有时会获得异步响应,有时会获得同步响应。相反,它们总是异步响应。

正如我在评论中所说,任何现实世界的编码情况都应该只对至少有时异步的操作使用promises。如果你的操作始终是同步的(如上面的例子那样),那么它们就不应该使用promises,因为promises只会使同步操作比使用直接同步函数调用更复杂。

此外,循环中的多个承诺操作不会以任何给定的方式排序或协调。如果这些是真正的异步操作,它们可以按任何顺序完成,并且h变量的更新将具有不确定的顺序(这通常是一个问题,因此通常是一个糟糕的设计模式)。

答案 1 :(得分:1)

为了解决你的promises问题,你可以用then包装所有reduce方法并听取所有Promises完成,这是按顺序运行promises。

如果您愿意,也可以使用Promise.all()并行运行所有承诺。

function promise (a) {
    return new Promise (function (resolve, reject) {
        if (a % 2 === 0) resolve(a);
        else reject(a);
    });
};

var timer = function(name) {
    var start = new Date();
    return {
        stop: function() {
            var end  = new Date();
            var time = end.getTime() - start.getTime();
            console.log('Function:', name, 'finished in', time, 'ms');
        }
    };
};

console.log('Right result', Array.from({length : 10000}, (el, i)=> (i + 1) % 2 === 0 ? (i+1)*3 : (i+1)*2).reduce((a,b)=> a + b));

var t2 = timer('promise');
var h = 0;
Promise.all(Array.from({length : 10000}, (el, i)=> promise(i + 1).then(x =>{ h += x*3}).catch(x =>{ h += x*2 })
))
.then(()=>{
  console.log('h:', h);
  t2.stop();
});