JavaScript Promise 的范围陷阱
JavaScript scope pitfall with Promise
我有一个数组 B 包含这样的词
B = ['hello', 'love', 'peace']
我正在尝试创建一个 Promise 数组 P:
for(var i = 0; i<B.length; i++){
var line = B[i];
var p = new Promise(function(resolve, reject){
setTimeout(function(){
resolve(line)},2000
);
})
P.push(p);
}
Promise.all(P).then(function(data){
console.log('data',data);
})
我希望数组 data 将等于 ['hello', 'love', 'peace'] 但实际上数据 = ['peace' , 'peace', 'peace']
我阅读了一些关于 JavaScript 范围陷阱的帖子,但仍然无法弄清楚如何修复我的代码。
请帮帮我。提前致谢
如果将 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"
]
*/
Some reading material on JavaScript closures and block vs. function scope.
在 promise 中移动 inatialization 行
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);
})
如果你使用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,你应该 var line = B[i];
在函数 p
您的问题是 var
声明在 JavaScript 中被 提升 到最近的封闭函数块的顶部,或者到文件的顶部.这意味着:
for (/* some loop */) {
var line = someValue();
}
实际翻译成:
var line;
for (/* some loop */) {
line = someValue();
}
您需要将 var
绑定移动到 new Promise
闭包内或使用 ES2015+ 块绑定(let
或 const
)
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));
这会注销(200 毫秒后):
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"
}
]
None 的其他 es6 答案利用 let
在 for
循环中具有特殊范围规则。
只需在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
的三个回调访问它时递增到它的最终值)。
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)
我有一个数组 B 包含这样的词
B = ['hello', 'love', 'peace']
我正在尝试创建一个 Promise 数组 P:
for(var i = 0; i<B.length; i++){
var line = B[i];
var p = new Promise(function(resolve, reject){
setTimeout(function(){
resolve(line)},2000
);
})
P.push(p);
}
Promise.all(P).then(function(data){
console.log('data',data);
})
我希望数组 data 将等于 ['hello', 'love', 'peace'] 但实际上数据 = ['peace' , 'peace', 'peace']
我阅读了一些关于 JavaScript 范围陷阱的帖子,但仍然无法弄清楚如何修复我的代码。
请帮帮我。提前致谢
如果将 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"
]
*/
Some reading material on JavaScript closures and block vs. function scope.
在 promise 中移动 inatialization 行
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);
})
如果你使用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,你应该 var line = B[i];
在函数 p
您的问题是 var
声明在 JavaScript 中被 提升 到最近的封闭函数块的顶部,或者到文件的顶部.这意味着:
for (/* some loop */) {
var line = someValue();
}
实际翻译成:
var line;
for (/* some loop */) {
line = someValue();
}
您需要将 var
绑定移动到 new Promise
闭包内或使用 ES2015+ 块绑定(let
或 const
)
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));
这会注销(200 毫秒后):
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"
}
]
None 的其他 es6 答案利用 let
在 for
循环中具有特殊范围规则。
只需在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
的三个回调访问它时递增到它的最终值)。
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)