Promise flattening fails - why?

时间:2017-11-13 06:35:03

标签: javascript promise es6-promise

const simplePromise = i => {
    return new Promise(function(resolve, reject) {
        console.log(i);
        setTimeout(function(){
            resolve();
        }, 2000);
    });
}

var anchor = simplePromise(0);
for (var i=1; i<4; i++) {
    anchor = anchor.then(_ => simplePromise(i));
}

prints:

0
4
4
4
4

instead of:

0
1
2
3
4

1. Can someone explain why? and 2. tell me how to achieve this?

I can see that the first promise is executed (i=0), then the loop runs and then the value of i(=4) gets passed to the next promise. Shouldn't this be solved by having a function inside then (_ => simplePromise(i)) ?

2 个答案:

答案 0 :(得分:3)

It's happened due you use var. Try to change var to let and that fix your problem.

UPDATE

That problem more clearly explained at this article and this (scroll to section Difference Details -> Closure in Loop) and great explanation of let key word

EXPLANATION

Let take that piece of code:

for (var i = 0; i < 5; ++i) {
  setTimeout(function () {
    console.log(i); // output '5' 5 times
  }, 100);  
}

In that example each iteration create function with closure on variable i, which will be executed in the future. Problem is var declare variable which

...is scoped to the nearest function block and let is scoped to the nearest enclosing block, which can be smaller than a function block.

i.e. all of the created functions will create closure to the same variable. And when the execution time comes i === 5. And All of the function will print the same value.

How let solve that problem...

let in the loop can re-binds it to each iteration of the loop, making sure to re-assign it the value from the end of the previous loop iteration, so it can be used to avoid issue with closures.

答案 1 :(得分:1)

你的错误是JS中最常见的错误之一 - 如果不是 最常见的错误 - 在异步情况下使用for循环来操纵状态变量。你的承诺和你的循环不会同步运行。循环完成的速度比任何承诺都快。

然而,在你的promise回调中使用i,它只在循环完成后运行。不要那样做。有一些方法可以防止它,但这已经经常讨论过,我只会建议研究并阅读一些现有的答案。

我强烈怀疑你甚至不想循环固定的数字范围。你实际上有一系列的物品。

只需删除for循环并使用数组迭代工具来避开范围问题。 Array#reduce是这里的完美候选人。

const simplePromise = val => {
    return new Promise(function(resolve, reject) {
        setTimeout(function(){
            console.log(val);
            resolve(val);
        }, 200);
    });
}

var items = [0,1,2,3,4];

console.log("array iteration starts");

items
    .reduce((acc, i) => acc.then(_ => simplePromise(i)), Promise.resolve())
    .then(val => console.log("chain execution end w/ " + val));

console.log("array iteration done");

/*
    acc = Promise.resolve()
      then simplePromise(0)
        then simplePromise(1)
          then simplePromise(2)
            then simplePromise(3)
              then simplePromise(4)
                then console.log("...")
*/