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

promise change global variable in for loop

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

var Promise = require('bluebird');

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

function regular (a, cb) {
    if (a % 2 === 0) return cb(null, a);
    return cb(a);
}

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


var t = timer('regular');
var g = 0;
for (var i = 1; i < 10001; i++) {
    regular(i, function(odd, even) {
        if (odd) g = g + (odd * 2);
        else g = g + (even * 3);
    });
}
console.log('g:', g); // g: 125015000
t.stop();

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

有趣的是,promises 不会改变全局变量 'h',我怎样才能使它 return 与变量 'g' 的结果相同?

更新

这里是尝试获得最终结果的更改代码,但承诺的不确定性给了我们意想不到的结果。

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 
}

目前我给出预期输出的代码更加陌生。

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
}

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

Promise 总是异步调用它们的 .then().catch() 处理程序。即使立即解决,它们也​​会让当前 JS 线程执行完毕,并在 "next tick".

上异步调用 .then().catch() 处理程序

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

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

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

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

要用 promises 解决你的问题,你可以用 reduce 包装所有 then 方法并听取所有 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();
});