与Promise的JavaScript范围陷阱

时间:2016-07-01 03:47:12

标签: javascript es6-promise

我有一个数组B包含这样的单词

B = ['你好','爱','和平']

我正在尝试创建一个Promise数组P:

if (txt.equalsIgnoreCase("yes")) {
    ...
} else {
    ...
}

我希望数组数据等于['hello','love','peace'],但实际上数据= ['和平','和平','和平']

我阅读了一些有关JavaScript范围陷阱的帖子,但仍然无法弄清楚如何修复我的代码。

请帮帮我。提前致谢

6 个答案:

答案 0 :(得分:1)

如果将line初始化移动到promise回调的函数范围内,则不同的迭代不会相互影响:



B = ['hello', 'love', 'peace'];
P = [];

for (var i = 0; i < B.length; i++) {
    var p = new Promise(function(resolve, reject) {
        var line = B[i];
        setTimeout(function() {
            resolve(line)
        }, 2000);
    })
    P.push(p);
}

Promise.all(P).then(function(data) {
    console.log('data', data);
})

/*
data [
  "hello",
  "love",
  "peace"
]
*/
&#13;
&#13;
&#13;

<强> Some reading material on JavaScript closures and block vs. function scope.

答案 1 :(得分:1)

如果您使用es6,您的代码可能就像这样

let B = ['hello', 'love', 'peace'];

let P = [];

for(var i = 0; i<B.length; i++){
 let line = B[i];
 let p = new Promise(function(resolve, reject){
   setTimeout(function(){
     resolve(line)
   },2000);
 })
 P.push(p);
}

Promise.all(P).then(function(data){
 console.log('data',data);
})

如果你使用es5,你应该在功能p {/ p>中var line = B[i];

答案 2 :(得分:0)

在promise

中移动行初始化
var B= ['hello', 'love', 'peace'] ;
  var P = [] ;
   for(var i = 0; i<B.length; i++){

        var p = new Promise(function(resolve, reject){
           var line = B[i];
            setTimeout(function(){
                resolve(line)},2000
            );
        })
        P.push(p);
    }

    Promise.all(P).then(function(data){
        console.log('data',data);
    })

答案 3 :(得分:0)

您的问题是,var声明在JavaScript中被提升到最近的封闭功能块的顶部,或者文件的顶部。这意味着:

for (/* some loop */) {
  var line = someValue();
}

实际上已翻译成:

var line;
for (/* some loop */) {
  line = someValue();
}

您需要在var关闭内移动new Promise绑定或使用ES2015 +块绑定(letconst

&#13;
&#13;
var words = ['hello', 'love', 'peace'];
var promises = [];
for (let word of words) {
  let letBinding = word;  // ES2015+
  const constBinding = word;  // ES2015+
  var varBinding = word;  // ES3+
  var p = new Promise(resolve => {
    var closureBinding = word;  // ES3+
    setTimeout(() =>
      resolve({
        varBinding,
        closureBinding,
        letBinding,
        constBinding,
      }),
      200
    );
  })
  promises.push(p);
}

Promise.all(promises).then(data => console.log('data:', data));
&#13;
&#13;
&#13;

注销(200ms后):

data: [
  {
    "varBinding": "peace",
    "closureBinding": "hello",
    "letBinding": "hello",
    "constBinding": "hello"
  },
  {
    "varBinding": "peace",
    "closureBinding": "love",
    "letBinding": "love",
    "constBinding": "love"
  },
  {
    "varBinding": "peace",
    "closureBinding": "peace",
    "letBinding": "peace",
    "constBinding": "peace"
  }
]

答案 4 :(得分:0)

其他es6答案都没有利用letfor循环中具有特殊范围规则的优势。

只需在for循环语句中使用let(尝试运行它):

var B = ['hello', 'love', 'peace'], P = [];

for(let i = 0; i<B.length; i++){
    var p = new Promise(resolve => setTimeout(() => resolve(B[i])));
    P.push(p);
}

Promise.all(P).then(data => console.log(data)); // hello,love,peace

在es6中,let在每个循环中实例化不同的i。这是专门为解决这个陷阱而做的。

(陷阱是循环中的单个公共i在到达setTimeout的三个回调时间后才会增加到其最终值。)

答案 5 :(得分:0)

 Promise.all(
  ['hello', 'love',  'peace'].map(Promise.resolve.bind(Promise)))
.then(console.log.bind(console))

或使用es7绑定语法:

Promise.all(['hello', 'love', 'peace'].map(::Promise.resolve)).then(::console.log)