Scope des variables en Javascript

Le Array qui ne voulait pas se remplir.

a marqué ce sujet comme résolu.

Bonjour, je suis en train de découvrir l’écosystème Javascript et dans mon petit scraper, le Array "articleArray" reste de longueur 1 (le dernier élément ajouté écrase le précédent). Lorsque je le déclare en global, il se comporte correctement, pouvez vous m’indiquer pourquoi ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var promiseRequestForArticles = function(articlesLinks){
  return new Promise(function(resolve, reject) {
    let articleArray = new Array();
    for (j=0; j<articlesLinks.length; j++){
      $.ajax(
        { url: articlesLinks[j],
          dataType: 'html',
          data: '',
          success : function(response) {
            var contentDiv = $(response).find('#content');
            var article = {
              images: contentDiv.first("div.body").text()
            };
            articleArray.push(article);
          }
        });
      }
      console.log(articleArray.length)
      resolve(articleArray)
    })
  }


function parseContent(){
      for (n=0; n<articlesLinksTags.length; n++){ 
      articleList = new Array();
      promiseRequestForArticles(articlesLinks.slice(2, 3)).then(function(value) {
        articleList.push(value);
      });
    }
    return articleList
  }

var jj =  parseContent();

`

J’ai pourtant lu attentivement la doc au sujet de let, et celle ci précise bien que son scope se situe dans le bloc courant, pourquoi donc diable ce comportement ? Je sors bien de la boucle avant mon resolve !

Votre aide est la bienvenue.

Tu n’as pas vraiment un problème de scope, tu n’as pas bien compris les promises en revanche.

J’ai un peu nettoyé ton exemple pour te montrer :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const createPromise = (n) =>
  new Promise((resolve, reject) => {
    const articleArray = []
    for (let j = 0; j < n; j++) {
      $.ajax({
        url: 'https://zestedesavoir.com',
        dataType: 'html',
        data: '',
        success (response) {
          setTimeout(() => {
            articleArray.push(`foo-${j}`)
          }, 500 * j)
        }
      })
    }
    resolve(articleArray)
  })


function parseContent(n) {
  createPromise(n).then((value) => {
    console.error(value)
  })
}

parseContent(3)

Ouvre la console de ton navigateur sur ce topic et colle le code ci-dessus.

Le principal problème de ton code c’est que success est un callback, $.ajax est async. Ton resolve l.16 est potentiellement appelé avant que tous tes $.ajax appellent leur callback success. J’ai mis ce problème en évidence en ajoutant un timeout.

Ce que tu devrais faire, c’est virer le for, faire un .map sur ton articlesLinks qui retourne une promise par élément d’articlesLinks. Chacun appelle son resolve dans son success. Après ça, tu peux utiliser Promise.all() sur ta liste de promises.

+3 -0

Merci bien, je dois dire que j’ai encore du mal à appréhender le flux d’exécution en JavaScript. Je comprend maintenant pourquoi de rendre mes variables globales corrigeait une partie du pro blême (et apportait moults effets de bords).

Si je reprend ton exemple, cela donnerais donc:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let createPromise = (n) =>
  new Promise((resolve, reject) => {
      $.ajax({
        url: 'https://zestedesavoir.com',
        dataType: 'html',
        data: '',
        success (response) {
          setTimeout(() => {
            resolve(`foo-${n}`)
          }, 500 * n)
        }
      })
    }
  )


function parseContent(n) {
   let nArray = [...Array(n).keys()];
   Promise.all(
        nArray.map(nA => createPromise(nA))).then(
            (value)=>{
            console.log(value);
   });
}

parseContent(3)

Et pour mon soucis:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var promiseRequestForArticles = function(url){
  return new Promise(function(resolve, reject) {
      $.ajax(
        { url: url,
          dataType: 'html',
          data: '',
          success : function(response) {
            var contentDiv = $(response).find('#content');
            var article = {
              images: contentDiv.first("div.body").text()
            };
            resolve(article);
          }
        });
    })
  }

function parseContent(urlList){
      articleList = new Array();
      promiseRequestList = urlList.map( url => promiseRequestForArticles(url)
      Promise.all(promiseRequestList).then(function(value) {
        articleList.push(value);
      });
    return articleList
  }
var jj =  parseContent(urlList);

Merci, avec quelques belles promesses et un peu d’amour, l’asynchrone paraît plus clair.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte