Native JS implementiert Promise (detaillierte Erklärung)

Zusammenfassung

Zunächst einmal ist Promise ein wichtiger Wissenspunkt in Asynchronie, und der beste Weg, es zu lernen, besteht darin, seine Grundprinzipien zu beherrschen.
In diesem Artikel geht es also hauptsächlich darum, wie Sie mit JS ein eigenes Versprechen einlösen können.

Konstrukteur

Schauen wir uns zunächst an, wie wir Promises verwenden, die wir beim Instanziieren von Objekten verwenden:

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

Wenn wir also unsere eigenen Klassen erstellen, müssen wir uns überlegen, wie wir diesen Parameter verwenden.


Werfen wir einen Blick darauf, wenn new Promise eine Callback-Funktion übergeben wird, sollte der Code in dieser Callback-Funktion sofort ausgeführt werden.

In dieser Callback-Funktion gibt es auch diese beiden Parameter resolve und verwerfen (ebenfalls eine Callback-Funktion).

In unserem Konstruktor sollten also diese beiden Funktionen resolve und replies vorhanden sein (egal, was diese beiden Funktionen jetzt tun).


Wir wissen, dass das Versprechen drei Eigenschaften hat:

ausstehend : zu bestimmen
erfüllt : entspricht der Auflösungsfunktion
abgelehnt : entspricht der Ablehnungsfunktion

Und sobald der Status geändert wurde, kann er nicht erneut geändert werden.
Unser Konstruktor sollte also eine Eigenschaft haben, die den aktuellen Zustand des Promise darstellt .


Wir wissen, dass eine res -Variable als Ergebniswert übergeben wird, unabhängig davon, ob „resolve“ oder „reject“ verwendet wird. Daher verwenden wir ein Attribut, um den Ergebniswert von „resolve“ und „reject“ zu speichern .

Schließlich können wir einen Konstruktor wie diesen entwerfen:

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

dann und Fangmethoden

Sehen wir uns zunächst an, wie Yiha diese beiden Methoden verwendet:

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

Im obigen Code können wir sehen, dass sowohl die then- als auch die catch-Methode eine Callback-Funktion akzeptieren,
und der Parameter dieser Callback-Funktion ist die zuvor definierte Datei this.res .

Wir können uns also vorstellen, Folgendes zu tun:

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

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

Aber diese Methode kann keine verketteten Aufrufe implementieren , das heißt, die then-Methode kann nicht nacheinander verwendet werden.
Aber wenn ich dieses Muster implementieren möchte, sollten wir ein Objekt unter der then-Methode zurückgeben, und dieses Objekt ist normalerweise this .
Können wir das also direkt zurückgeben ?Siehe die Situation unten.

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

Wenn unter der Then-Methode ein neues Promise zurückgegeben wird, können wir dies nicht direkt in der Then-Methode zurückgeben .

Wir sollten also zuerst beurteilen, ob die Callback-Funktion von then ein neues Objekt zurückgibt, und wenn nicht, das this- Objekt des aktuellen then zurückgeben .

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

Lösen Sie das asynchrone Problem

Der obige Code scheint kein Problem zu haben, aber wenn ich so schreibe:

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

Stellt sich die große Frage, warum? Denn als ich in p2.then war , lief der Timer nicht zu Ende, also ist der Zustand von p2 immer noch ausstehend und es wird überhaupt nicht weitergehen.

Hier verwenden wir einen klassischen Lösungsmodus, der oft in Axios und Routing zu sehen ist, bevor ich ihn geschrieben habe.

In der then-Methode speichern wir, wenn der aktuelle Zustand anhängig ist (dieser Satz ist sehr wichtig o), die aktuelle Callback-Funktion (nicht unbedingt eine, es können dann mehrere sein, also verwenden wir zum Speichern ein Array).

Wann speichern wir es dann und verwenden es? Natürlich nach Ablauf des Timers! Wann endet der Timer? Wenn sich der aktuelle Zusagestatus ändert , müssen wir diese Methoden natürlich in den Methoden resolve und replies aufrufen! ! !

Also müssen wir den Konstruktor ändern:

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

Dann ändern Sie unsere then- und catch-Methoden:

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

Auf diese Weise lösen wir das Problem der Asynchronität.

alle und Rennmethoden

Es ist immer noch dasselbe, lassen Sie uns überprüfen, wie es verwendet wird, bevor Sie schreiben:

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

Dann wissen wir, dass beide ein Array als Parameter nehmen, und wir betrachten hier keine anderen Situationen, ich denke nur, dass das Array voller Promise-Objekte ist. . .

Der Unterschied zwischen den beiden ist:
all : Wenn alle Versprechen erfüllt sind und der Status erfüllt ist , ist das von der Methode all zurückgegebene Versprechen erfüllt, andernfalls wird es abgelehnt.

race : Das erste Promise-Objekt mit einem Ergebnis ist das Ergebnis des von race zurückgegebenen Promises.


Lassen Sie uns nun darüber nachdenken, wie die all-Methode implementiert wird Nachdem wir die Array-Parameter erhalten haben, müssen wir sie erneut durchlaufen.

Rufen Sie dann die then-Methode und die catch-Methode für jedes Element auf.

Die then-Methode muss über ein Ergebnisarray verfügen, das den Ergebniswert jedes Promise enthält.
Wir können einen Zähler verwenden, um die Anzahl der Aufrufe der then-Methode zu zählen.Wenn die Größe des Zählers gleich der Länge des Arrays ist, beweist dies, dass alle Versprechungen erfüllt sind und ein Array von Ergebnissen zurückgegeben werden kann.

Solange die catch-Methode einmal aufgerufen wird, wird das Ergebnis direkt zurückgegeben, nicht viele bb, einfach direkt zurückkehren

Denken Sie schließlich daran, das neue Versprechen an o zurückzugeben.

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
}

Wenn die Race-Methode verwendet wird, ist sie möglicherweise einfacher zu implementieren. Unabhängig davon, ob die then-Methode oder die catch-Methode des Promise ausgelöst wird, wird das Ergebnis direkt zurückgegeben:

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
}

Guess you like

Origin blog.csdn.net/weixin_46726346/article/details/119731436