Maison > interface Web > js tutoriel > le corps du texte

Qu'est-ce que le contrôle de concurrence ? Comment implémenter le contrôle de concurrence en JavaScript ?

青灯夜游
Libérer: 2021-06-28 10:50:55
avant
3011 Les gens l'ont consulté

Qu'est-ce que le contrôle de concurrence ? Comment implémenter le contrôle de concurrence en JavaScript ?

Dans le processus de développement quotidien, vous pouvez rencontrer des scénarios de contrôle de concurrence, tels que le contrôle du nombre de requêtes simultanées. Alors, comment implémenter le contrôle de concurrence en JavaScript ? Avant de répondre à cette question, introduisons brièvement le contrôle de concurrence.

Supposons qu'il y ait 6 tâches à effectuer à exécuter et que nous souhaitions limiter le nombre de tâches pouvant être exécutées en même temps, c'est-à-dire qu'au plus 2 tâches peuvent être exécutées en même temps . Lorsqu'une tâche de la Liste des tâches d'exécution est terminée, le programme obtiendra automatiquement une nouvelle tâche à faire à partir de la Liste des tâches à faire et ajoutera la tâche à Exécution liste de tâches . Afin de permettre à chacun de comprendre le processus ci-dessus de manière plus intuitive, Abago a spécialement dessiné les 3 images suivantes :

1.1 Étape 1

Quest-ce que le contrôle de concurrence ? Comment implémenter le contrôle de concurrence en JavaScript ?

1.2 Phase 2

Quest-ce que le contrôle de concurrence ? Comment implémenter le contrôle de concurrence en JavaScript ?

1.3 Phase 3

Quest-ce que le contrôle de concurrence ? Comment implémenter le contrôle de concurrence en JavaScript ?

D'accord , après avoir introduit le contrôle de concurrence, Brother Abao utilisera la bibliothèque async-pool sur Github pour introduire l'implémentation spécifique du contrôle de concurrence des tâches asynchrones.

async-pool : https://github.com/rxaviers/async-pool

Exécutez plusieurs fonctions de retour de promesse et asynchrones avec une concurrence limitée à l'aide d'ES6/ natif ES7.

2. Implémentation du contrôle de concurrence

async-pool Cette bibliothèque fournit deux versions différentes d'implémentation, ES7 et ES6 après les avoir analysées avant. l'implémentation spécifique, voyons comment l'utiliser.

2.1 Utilisation de asyncPool

const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
await asyncPool(2, [1000, 5000, 3000, 2000], timeout);
Copier après la connexion

Dans le code ci-dessus, nous utilisons la fonction async-pool fournie par cette bibliothèque pour implémenter la concurrence asynchrone des tâches contrôle. asyncPool La signature de la fonction est la suivante : asyncPool

function asyncPool(poolLimit, array, iteratorFn){ ... }
Copier après la connexion

La fonction reçoit 3 paramètres :

  • (type numérique) : représente le nombre de limites de concurrence ; 🎜> poolLimit
  • (type de tableau) : représente le tableau de tâches ;
  • array
  • (type de fonction) : représente la fonction itérative, qui est utilisée pour traiter chaque élément de tâche. un objet de promesse ou une fonction asynchrone.
  • iteratorFn
  • Pour l'exemple ci-dessus, après avoir utilisé la fonction
, le processus d'exécution correspondant est le suivant :

const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
await asyncPool(2, [1000, 5000, 3000, 2000], timeout);
// Call iterator (i = 1000)
// Call iterator (i = 5000)
// Pool limit of 2 reached, wait for the quicker one to complete...
// 1000 finishes
// Call iterator (i = 3000)
// Pool limit of 2 reached, wait for the quicker one to complete...
// 3000 finishes
// Call iterator (i = 2000)
// Itaration is complete, wait until running ones complete...
// 5000 finishes
// 2000 finishes
// Resolves, results are passed in given array order `[1000, 5000, 3000, 2000]`.
Copier après la connexion
asyncPoolEn observant les informations d'annotation ci-dessus, nous pouvons approximativement comprendre le contrôle circuler à l'intérieur de la fonction

. Analysons d'abord l'implémentation ES7 de la fonction

. asyncPoolasyncPool

Suivez "Full Stack Road to Immortality" et lisez 4 livres électroniques gratuits créés à l'origine par Brother A Bao (avec un téléchargement cumulé de plus de 30 000) et plus de 50 didacticiels de la série TS.

2.2 Implémentation d'asyncPool ES7
async function asyncPool(poolLimit, array, iteratorFn) {
  const ret = []; // 存储所有的异步任务
  const executing = []; // 存储正在执行的异步任务
  for (const item of array) {
    // 调用iteratorFn函数创建异步任务
    const p = Promise.resolve().then(() => iteratorFn(item, array));
    ret.push(p); // 保存新的异步任务

    // 当poolLimit值小于或等于总任务个数时,进行并发控制
    if (poolLimit <= array.length) {
      // 当任务完成后,从正在执行的任务数组中移除已完成的任务
      const e = p.then(() => executing.splice(executing.indexOf(e), 1));
      executing.push(e); // 保存正在执行的异步任务
      if (executing.length >= poolLimit) {
        await Promise.race(executing); // 等待较快的任务执行完成
      }
    }
  }
  return Promise.all(ret);
}
Copier après la connexion

Dans le code ci-dessus, nous utilisons pleinement les fonctionnalités des fonctions

et

, combinées à la fonctionnalité Promise.all fournie dans ES7, et enfin Implémentation de la fonction de contrôle de concurrence. À l'aide de la ligne d'instruction Promise.race, nous attendrons que la tâche la plus rapide de la async awaitliste des tâches d'exécutionawait Promise.race(executing); se termine avant de continuer à exécuter la boucle suivante. La mise en œuvre d'asyncPool ES7 est relativement simple. Voyons ensuite comment obtenir la même fonction sans utiliser la fonctionnalité

.

async await2.3 Implémentation d'asyncPool ES6

function asyncPool(poolLimit, array, iteratorFn) {
  let i = 0;
  const ret = []; // 存储所有的异步任务
  const executing = []; // 存储正在执行的异步任务
  const enqueue = function () {
    if (i === array.length) {
      return Promise.resolve();
    }
    const item = array[i++]; // 获取新的任务项
    const p = Promise.resolve().then(() => iteratorFn(item, array));
    ret.push(p);

    let r = Promise.resolve();

    // 当poolLimit值小于或等于总任务个数时,进行并发控制
    if (poolLimit <= array.length) {
      // 当任务完成后,从正在执行的任务数组中移除已完成的任务
      const e = p.then(() => executing.splice(executing.indexOf(e), 1));
      executing.push(e);
      if (executing.length >= poolLimit) {
        r = Promise.race(executing); 
      }
    }
 
    // 正在执行任务列表 中较快的任务执行完成之后,才会从array数组中获取新的待办任务
    return r.then(() => enqueue());
  };
  return enqueue().then(() => Promise.all(ret));
}
Copier après la connexion

Dans la version d'implémentation ES6, la logique de contrôle de base est implémentée via la fonction

encapsulée en interne. Lorsque l'objet

renvoyé par enqueue sera terminé, la fonction Promise.race(executing) sera appelée pour obtenir une nouvelle tâche à faire à partir du tableau Promise. enqueuearray3. Frère Abao a quelque chose à dire

Dans l'implémentation spécifique de ES7 et ES6 de la bibliothèque

, nous avons utilisé les fonctions

et asyncPool. Parmi eux, l'écriture manuscrite Promise.all est une question d'entretien courante. Profitant de cette opportunité, frère A Bao a rejoint tout le monde dans l'écriture manuscrite de versions simples des fonctions Promise.race et Promise.all. Promise.all

3.1 手写 Promise.all

Promise.all(iterable) 方法会返回一个 promise 对象,当输入的所有 promise 对象的状态都变成 resolved 时,返回的 promise 对象就会以数组的形式,返回每个 promise 对象 resolve 后的结果。当输入的任何一个 promise 对象状态变成 rejected 时,则返回的 promise 对象会 reject 对应的错误信息。

Promise.all = function (iterators) {
  return new Promise((resolve, reject) => {
    if (!iterators || iterators.length === 0) {
      resolve([]);
    } else {
      let count = 0; // 计数器,用于判断所有任务是否执行完成
      let result = []; // 结果数组
      for (let i = 0; i < iterators.length; i++) {
        // 考虑到iterators[i]可能是普通对象,则统一包装为Promise对象
        Promise.resolve(iterators[i]).then(
          (data) => {
            result[i] = data; // 按顺序保存对应的结果
            // 当所有任务都执行完成后,再统一返回结果
            if (++count === iterators.length) {
              resolve(result);
            }
          },
          (err) => {
            reject(err); // 任何一个Promise对象执行失败,则调用reject()方法
            return;
          }
        );
      }
    }
  });
};
Copier après la connexion

需要注意的是对于 Promise.all 的标准实现来说,它的参数是一个可迭代对象,比如 Array、String 或 Set 等。

3.2 手写 Promise.race

Promise.race(iterable) 方法会返回一个 promise 对象,一旦迭代器中的某个 promise 对象 resolvedrejected,返回的 promise 对象就会 resolve 或 reject 相应的值。

Promise.race = function (iterators) {
  return new Promise((resolve, reject) => {
    for (const iter of iterators) {
      Promise.resolve(iter)
        .then((res) => {
          resolve(res);
        })
        .catch((e) => {
          reject(e);
        });
    }
  });
};
Copier après la connexion

本文阿宝哥带大家详细分析了 async-pool 异步任务并发控制的具体实现,同时为了让大家能够更好地理解 async-pool 的核心代码。最后阿宝哥还带大家一起手写简易版的 Promise.allPromise.race 函数。其实除了 Promise.all 函数之外,还存在另一个函数 —— Promise.allSettled,该函数用于解决 Promise.all 存在的问题,感兴趣的小伙伴可以自行研究一下。

四、参考资源

更多编程相关知识,请访问:编程视频!!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:juejin.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!