Native JS implémente Promise (explication détaillée)

Résumé

Tout d'abord, Promise est un point de connaissance important en asynchronie, et la meilleure façon de l'apprendre est de maîtriser ses principes de base.
Cet article parle donc principalement de la façon d'utiliser JS pour réaliser votre propre promesse.

Constructeur

Voyons d'abord comment nous utilisons les promesses, que nous utilisons lors de l'instanciation d'objets :

    let p1 = new Promise((resolve, reject) => {
    
    
      let random = Math.floor(Math.random() * 10);
      if (random > 4) {
    
    
        resolve('sucess')
      } else {
    
    
        reject('erro')
      }
    })

Ainsi, lorsque nous créons nos propres classes, nous devons réfléchir à la manière d'utiliser ce paramètre.


Jetons un coup d'œil, lorsque new Promise reçoit une fonction de rappel, le code de cette fonction de rappel doit être exécuté immédiatement.

Dans cette fonction de rappel, il y a aussi ces deux paramètres résoudre et rejeter (également une fonction de rappel).

Donc, dans notre constructeur, il devrait y avoir ces deux fonctions de résolution et de rejet (peu importe ce que ces deux fonctions font pour l'instant).


Nous savons que la promesse a trois propriétés :

en attente : à déterminer
rempli : correspondant à la fonction de résolution
rejeté : correspondant à la fonction de rejet

Et une fois que l'état est changé, il ne peut plus être changé.
Notre constructeur devrait donc avoir une propriété représentant l'état actuel de la promesse .


Nous savons qu'indépendamment de l'utilisation de la résolution ou du rejet, une variable res sera transmise comme valeur de résultat. Nous utilisons donc un attribut pour enregistrer la valeur de résultat de resolve et de rejet .

Enfin, nous pouvons concevoir un constructeur comme celui-ci :

function Mypromise (config) {
    
    
  this.status = 'pending';
  this.res = ''
  let resolve = (data) => {
    
    
    this.status = 'fulfilled';
    this.res = data
  }
  let reject = (data) => {
    
    
    this.status = 'rejected';
    this.res = data
  }

  config(resolve, reject)
}

méthodes then et catch

Voyons d'abord comment Yiha utilise ces deux méthodes :

    p1
      .then(res => {
    
    
        console.log(res);
      })
      .then(res => {
    
    
        console.log(res);
      })
      .catch(err => {
    
    
        console.log(err);
      })

Dans le code ci-dessus, nous pouvons voir que les méthodes then et catch acceptent une fonction de rappel,
et le paramètre de cette fonction de rappel est le this.res que nous avons défini précédemment .

On peut donc penser à faire ceci :

Mypromise.prototype.then = function (config) {
    
    
  if (this.status == 'fulfilled') {
    
    
    config(this.res)
  }
}

Mypromise.prototype.catch = function (config) {
    
    
  if (this.status == 'rejected') {
    
    
    config(this.res)
  }
}

Mais cette méthode ne peut pas implémenter les appels chaînés , c'est-à-dire que la méthode then ne peut pas être utilisée consécutivement.
Mais si je veux implémenter ce modèle, nous devons retourner un objet sous la méthode then, et cet objet est normalement this .
Pouvons-nous retourner cela directement ? Voir la situation ci-dessous.

p1
  .then(res => {
    
    
    console.log(res);
    return new Promise((resolve, reject) => {
    
    
      resolve('1111')
    })
  })
  .then(res => {
    
    
    console.log(res);
  })
  .catch(err => {
    
    
    console.log(err);
  })

Si une nouvelle promesse est renvoyée sous la méthode then, nous ne pouvons pas directement la renvoyer directement dans la méthode then .

Nous devons donc d'abord juger si la fonction de rappel de then renvoie un nouvel objet, et si ce n'est pas le cas, renvoyer l' objet this du courant then.

Mypromise.prototype.then = function (config) {
    
    
  if (this.status == 'fulfilled') {
    
    
    var res = config(this.res)
  }
  return res || this;
}

Mypromise.prototype.catch = function (config) {
    
    
  if (this.status == 'rejected') {
    
    
    var res = config(this.res)
  }
  return res || this;
}

Résoudre le problème asynchrone

Le code ci-dessus semble n'avoir aucun problème, mais si j'écris comme ceci:

    let p2 = new Mypromise((resolve, reject) => {
    
    
      setTimeout(() => {
    
    
        reject('p2 resolve')
      }, 1000);
    })

La grande question se pose, pourquoi ? Parce que quand j'étais dans p2.then , la minuterie n'a pas fini de s'exécuter, donc l'état de p2 est toujours en attente et il ne continuera pas du tout.

Ici, nous utilisons un mode de solution classique, que l'on peut souvent voir dans les axios et le routage avant que je l'écrive.

Dans la méthode then, si l'état actuel est en attente (cette phrase est très importante o), nous sauvegardons la fonction de rappel actuelle (pas nécessairement une, il peut y en avoir plusieurs alors, nous utilisons donc un tableau pour sauvegarder).

Alors, quand le sauvegardons-nous et l'utilisons-nous? Une fois le temps écoulé, bien sûr ! Quand le chronomètre se termine-t-il ? Bien sûr, lorsque l'état actuel de la promesse change , nous devons appeler ces méthodes dans les méthodes de résolution et de rejet ! ! !

Il faut donc modifier le constructeur :

function Mypromise (config) {
    
    
  this.status = 'pending';
  this.res = '';
  this.saveResolve = [];
  this.saveReject = [];
  let resolve = (data) => {
    
    
    if (this.status == 'pending') {
    
    
      this.status = 'fulfilled';
      this.res = data
      this.saveResolve.forEach(val => {
    
    
        val(this.res)
      })
    }
  }
  let reject = (data) => {
    
    
    if (this.status == 'pending') {
    
    
      this.status = 'rejected';
      this.res = data
      this.saveReject.forEach(val => {
    
    
        val(this.res)
      })
    }
  }
  config(resolve, reject);
}

Modifiez ensuite nos méthodes then et catch :

Mypromise.prototype.then = function (config) {
    
    
  if (this.status == 'pending') {
    
    
    this.saveResolve.push(config);
  }
  if (this.status == 'fulfilled') {
    
    
    var res = config(this.res)
  }
  return res || this;
}

Mypromise.prototype.catch = function (config) {
    
    
  if (this.status == 'pending') {
    
    
    this.saveReject.push(config)
  }
  if (this.status == 'rejected') {
    
    
    var res = config(this.res)
  }
  return res || this;
}

De cette façon, nous résoudrons le problème de l'asynchronisme.

toutes et méthodes de course

C'est toujours pareil, passons en revue comment l'utiliser avant d'écrire :

    Mypromise.all([p2, p3, p4])
      .then(res => {
    
    
        console.log(res);
      })
      .catch(err => {
    
    
        console.log(err);
      })

    Mypromise.race([p2, p3, p4])
      .then(res => {
    
    
        console.log(res);
      })
      .catch(err => {
    
    
        console.log(err);
      })

Ensuite, nous savons que les deux prennent un tableau comme paramètre, et nous ne considérons pas d'autres situations ici, je pense juste que le tableau est plein d'objets de promesse. . .

La différence entre les deux est :
all : lorsque toutes les promesses sont remplies et que le statut est rempli , la promesse renvoyée par la méthode all est remplie, sinon elle est rejetée.

race : Le premier objet de promesse avec un résultat est le résultat de la promesse renvoyée par race.


Réfléchissons maintenant à la façon d'implémenter la méthode all.Après avoir obtenu les paramètres du tableau, nous devons le parcourir à nouveau.

Appelez ensuite la méthode then et la méthode catch pour chaque élément.

La méthode then doit avoir un tableau de résultats pour contenir la valeur de résultat de chaque promesse.
Nous pouvons utiliser un compteur pour compter le nombre d'invocations de la méthode then. Si la taille du compteur est égale à la longueur du tableau, cela prouve que toutes les promesses sont remplies et un tableau de résultats peut être renvoyé.

Tant que la méthode catch est appelée une fois, le résultat sera renvoyé directement, pas beaucoup de bb, il suffit de revenir directement

Enfin pensez à retourner la nouvelle promesse à o.

Mypromise.all = function (arr) {
    
    
  let result = [];
  let count = 0;
  let promise = new Mypromise((resolve, reject) => {
    
    
    for (var i = 0; i < arr.length; i++) {
    
    
      arr[i]
        .then(res => {
    
    
          result.push(res);
          count++;
          if (count == arr.length) resolve(result);
        })
        .catch(err => {
    
    
          reject(err)
        })
    }
  })

  return promise
}

Si la méthode race est utilisée, elle peut être plus simple à implémenter, que la méthode then ou la méthode catch de la promesse soit déclenchée, le résultat sera retourné directement :

Mypromise.race = function (arr) {
    
    
  let promise = new Mypromise((resolve, reject) => {
    
    
    for (var i = 0; i < arr.length; i++) {
    
    
      arr[i]
        .then(res => {
    
    
          resolve(res);
        })
        .catch(err => {
    
    
          reject(err)
        })
    }
  })

  return promise
}

Je suppose que tu aimes

Origine blog.csdn.net/weixin_46726346/article/details/119731436
conseillé
Classement