Technologieaustausch

Wie das Frontend gleichzeitige Anfragen steuert

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Wann muss das Frontend gleichzeitige Anforderungen steuern? Wenn die Anforderung der erforderlichen Daten mehrmals erforderlich ist. Wenn die Schnittstelle beispielsweise eine große Datenmenge auf einmal zurückgibt, bleibt der Browser beim Rendern hängen oder stürzt sogar ab. Zu diesem Zeitpunkt können wir 6 Anfragen gleichzeitig in Stapeln ausgeben, um ein Hängenbleiben oder einen Absturz zu vermeiden .

Wie steuert das Frontend gleichzeitige Anfragen?

Schlüsselideen für die Front-End-Steuerung gleichzeitiger Anfragen

Wenn es beispielsweise 20 Anfragen gibt, müssen Sie diesen folgen 3 Nachdem die erste Gruppe die Anfrage abgeschlossen hat, wird die zweite Gruppe angefordert und so weiter.

Die Schlüsselidee besteht darin, die Anforderungsmethode und die Anforderungsparameter in einem Array zu speichern, dann jeweils drei anzufordern und nach Abschluss der Anforderung die nächsten drei anzufordern. Nachdem jede Gruppe von Anforderungen zurückgegeben wurde, werden die Ergebnisse gespeichert, und nachdem alle Anforderungen zurückgegeben wurden, werden alle Ergebnisse zurückgegeben.

API-Design

pControl : Concurrent-Request-Controller, der die maximale Anzahl von Parallelitäten weitergibt;
add : Anfrage und Parameter hinzufügen;
start : Anfrage starten, Versprechen zurückgeben, bestehen, nachdem die Anfrage abgeschlossen ist.then Erhalten Sie alle Ergebnisse;

Code

function pControl(limit) {
  const taskQueue = [] // {task: Function, params: any[]}[]
  return {
    add,
    start
  }

  function add(task, params) {
    taskQueue.push({
      task,
      params
    })
  }

  function start() {
    return runAllTasks()
  }

  function runAllTasks() {
    const allResults = []
    return new Promise((resolve) => {

      runTask()

      function runTask() {
        if (taskQueue.length === 0) {
          // 递归结束
          return resolve(allResults)
        }
        const needRunSize = Math.min(taskQueue.length, limit)
        const tasks = taskQueue.splice(0, needRunSize)
        const promises = tasks.map(({
          task,
          params
        }) => task(params))
        Promise.all(promises).then((resList) => {
          allResults.push(...resList)
          // NOTE 递归调用的位置很关键
          runTask()
        })
      }
    })
  }
}
  • 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
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

Interpretation des Schlüsselcodes

  • pControl: Diese Funktion gibt ein Objekt zurück, das enthält add Undstart Zwei Methoden,add Wird zum Hinzufügen von Aufgaben und Parametern verwendet.start Wird zum Starten der Anfrage verwendet und ist ein Abschluss.

  • runAllTasks: gibt a zurückpromise, und dann reinnew PromiseIntern rekursiv ausgeführtrunTask, runTask ist erfolgreichPromise.all Gleichzeitige Anforderungen ausführen inPromise.all().then() erneut aufrufenrunTask, implementieren Sie einen Satz von Rückgabeanforderungen und führen Sie dann den zweiten Satz von Anforderungen aus.

Der Schlüssel zur Verwirklichung des Gruppenwartens liegt darin .then rekursiver Aufruf.

Überlegung: Kann runAllTasks mithilfe einer Schleife implementiert werden?

Ja, muss verwendet werden async 和 for 循环 + await

async function runAllTasks2() {
  const allResults = []
  const groupArr = []
  let startIndex = 0
  // 划分分组
  while (startIndex < taskQueue.length) {
    const arr = taskQueue.slice(startIndex, startIndex + limit)
    groupArr.push(arr)
    startIndex += limit
  }
  for (let index = 0; index < groupArr.length; index++) {
    const pList = groupArr[index].map(({
      task,
      params
    }) => task(params))
    const res = await Promise.all(pList)
    allResults.push(...res)
  }
  return allResults
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

Kann nicht in einer for-Schleife verwendet werden .then , andernfalls wartet die nächste Schleife nicht auf die vorherige Schleife.

verwenden for of Iterative Implementierung:

async function runAllTasks2() {
  const allResults = []
  const groupArr = []
  let startIndex = 0
  // 划分分组
  while (startIndex < taskQueue.length) {
    const arr = taskQueue.slice(startIndex, startIndex + limit)
    groupArr.push(arr)
    startIndex += limit
  }
  // 迭代分组
  const it = groupArr.entries()
  for (const [key, value] of it) {
    const pList = value.map(({
      task,
      params
    }) => task(params))
    const res = await Promise.all(pList)
    allResults.push(...res)
  }
  return allResults
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Wie verwende ich Schleifen und Promise zusammen?

forwhilefor...of Wenn Sie bei zwingenden Schleifenstrukturen den Warteeffekt in der Schleife erzielen möchten, müssen Sie verwendenasync Funktions-Wrapper in Schleifeawait ,Außer Betrieb.then

forEachmapfilter Funktionsschleifenstrukturen wie diese unterstützen keine Warteeffekte, da diese Funktionsschleifenstrukturen synchron sind und kein Warten unterstützen.

async Und循环 + await Kombiniert, um den Warteeffekt zwischen Schleifen zu erzielen.

promise.then Und递归 Kombiniert, um den Warteeffekt zwischen Schleifen zu erzielen.

Verbessern Sie die API, um die Verwendung zu vereinfachen

  1. Standardparameter festlegen: gebenpControlLegen Sie einen geeigneten Standardwert fest und setzen Sie ihn auf6Da derselbe Domänenname vorhanden ist, beträgt die Anzahl der gleichzeitigen Anforderungen des Browsers 6.
  2. Rückruf starten: Durch den Rückruf können Sie die Anforderungsergebnisse jeder Gruppe abrufen und die Anzahl der aktuell abgeschlossenen Anforderungen ermitteln.

Diese beiden Verbesserungen sind einfach. Schauen wir uns zunächst die Verwendung an:

const asyncTaskControl = pControl() // 默认 6 
asyncTaskControl.add(task, params1)
asyncTaskControl.add(task, params2)
// ...
asyncTaskControl.add(task, params10)

asyncTaskControl.start((res, doneSize) => {
  // 获取每组请求的结果 和当前完成了多少请求
  console.log(res) // [{index:number,result:data}] 
  console.log(doneSize)
}).then(allResults => {
  // 所有请求结果
  console.log(allResults)
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Was bewirkt der Start-Callback?

Für Benutzer ist es praktisch, die Ergebnisse aktueller gleichzeitiger Anforderungen abzurufen und den Abschlussfortschritt zu berechnen.

Kapseln Sie die oben genannten Funktionen in p-control Veröffentlichung des NPM-Pakets

npm: p-Steuerung

zugänglich npm i p-control Herunterladen und verwenden.

Zusammenfassung

  • .then Kombiniert mit Rekursion, um das Warten zwischen asynchronen Aufgaben zu erreichen;
  • forwhileWarten Sie auf die Schleife undasync + awaitWird in Kombination verwendet, um das Warten zwischen asynchronen Aufgaben zu erreichen.
  • verwendenPromise.allImplementieren Sie mehrere asynchrone Aufgaben zur gleichzeitigen Ausführung.