2016-07-01 7 views
1

Ich habe ein Array B enthält Worte wie dieseJavaScript Umfang pitfall mit Versprechen

B = [ 'hallo', 'Liebe', 'Frieden']

ich ein Versprechen Array P zu schaffen versuchen:

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

ich erwarte, dass Array Daten werden gleich [ 'Hallo', 'Liebe', 'Frieden'], aber eigentlich daten = ['frieden', 'frieden', 'frieden']

Ich habe ein paar Beiträge über JavaScript Bereich Fallstricke gelesen, kann aber immer noch nicht herausfinden, wie ich meinen Code beheben kann.

Bitte helfen Sie mir. Vielen Dank im Voraus

+0

So haben Sie eine sehr ruhige Versprechen. Witze beiseite, abgesehen davon, dass du 'let' benutzt, solltest du' line' wirklich in den 'Promise'-Executor-Bereich verschieben, wie es Timosta empfohlen hat. Sie brauchen hier keine Schließung, weil 'line' nur eine Hilfsvariable ist, die nicht im übergeordneten Bereich vorhanden sein muss. – ftor

Antwort

1

wenn Sie es6 verwenden, können Sie Code wie sein diese

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

, wenn Sie die ES5 verwenden, sollten Sie var line = B[i]; in Funktion p

+0

Super! Können Sie mir den Grund erklären, warum mein Code nicht funktioniert? –

+1

Der Unterschied ist das Scoping. var ist auf den nächsten Funktionsblock beschränkt, und let ist auf den nächsten umschließenden Block beschränkt (beide sind global, wenn sie außerhalb eines Blocks liegen), der kleiner als ein Funktionsblock sein kann. –

2

Wenn Sie die line Initialisierung in den Funktionsumfang der Versprechen Rückruf bewegen, werden die verschiedenen Iterationen nicht gegenseitig beeinflussen:

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.

0

verschieben Sie die Linie Inatialisierung innerhalb Versprechen

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

Ihr Problem ist, dass var Deklarationen sind hochgezogen in JavaScript an die Spitze der nächsten umschließenden Funktion n Block oder an den Anfang der Datei. Das bedeutet:

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

tatsächlich in übersetzt:

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

entweder Sie benötigen die var Bindung in Ihrem new Promise Verschluss zu bewegen oder eine ES2015 + Block verwenden Bindung (let oder 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));

Diese abmeldet (nach 200 ms):

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" 
    } 
] 
0

Keiner der anderen es6 Antworten Vorteil let mit speziellen Scoping-Regeln in for Schleifen nehmen.

Verwenden Sie einfach let in der for-Schleife-Anweisung (versuchen Sie es zu laufen):

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

In es6, let instanziiert eine andere i in jeder Schleife. Dies wurde speziell gemacht, um diese Falle anzugehen.

(Der Nachteil ist, dass eine einzige gemeinsame i in der Schleife auf den endgültigen Wert inkrementiert hätte, bis die drei Rückrufe setTimeout Sekunden später darauf zugreifen).

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

oder mit ES7 binden Syntax:

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