노드js-Paralelismo limitado com 배열.지도.

Créditos da Imagem
OArray.map ()èuma funçO muitoútil,mas,infelizmente,sófunciona com funçes síncronas.Uma Solu231;o simples para executar funçesasyncéusar oPromose.all()ou seu irmão mais tolerantePromise.allSettled():
// Falha no momento que UMA função do .map falhar
const results = await Promise.all(array.map(asynMapFunction))

// Continua executando mesmo se uma função falhar
const results = await Promise.allSettled(array.map(asynMapFunction))
함수 aassim:o.map()iráconvertercada item do array emuma Promise,então teremosum array de Promises para resolver.Háduas maneiras de fazer isso:
  • Promise.all():joga um erro se a funão no.지도jogarumerro(MDN
  • Promise.allSettled(): funöes no로 실행합니다.맵emtodoo 진열, mesmoque algumas delas joguemumerro(MDN
  • Portanto,a saída do.allSettled()éum array de objetos que informa se a execuão falhou ou não.
    Cada objeto na saída de.allSettled()éparecido com este:
    // Quando a função roda com sucesso
    {
      status: "fulfilled",
      value: // o valor retornado da função do .map
    }
    
    // Quando a função joga um erro
    {
      status: "rejected",
      reason: // o erro jogado pela função do .map
    }
    
    Porém, háum problema: ao contr á rio de um.map()'정상'입니다. 왜냐하면 fun ões de mapan ão ser ão executadas em série 때문입니다.funöesasync로mapaestarãoexecutandoaomesmotempo를 합니다.Embora o JavaScript seja normalmente uma linguagem de threadúnica,isso signific que os recursos alocados(como memória e portas)para cada funão serão ocupados atéque as Promises sejam resolvidas ou rejeitadas.Para array enormes,no entanto,vamos executar um grande numero de funões mapa ao mesmo tempo.Isso pode Potentialmente:
  • Consumir muita memória,pois cada funão-mapa mantém todas as suas variáveis​​enquanto estiver em execuão.Se vocêestiver executando lambda,por exemplo,ele pode facilmente travar seu tempo de execuão(ou vocêtem que pagar o custo de atingirum tempo de execuão mais robusto)
  • Atingir os limites de taxa:se o mapa estiver acessando uma API para cada funão,a API pode Retronar um erro pela quantidade alta de requisiçes
  • Seria bom se pudéssemos de alguma forma limitar essas execuçes paralelas.Uma opçoéusar afunção eachLimit do popular módulo async .너는 우리의 생활이 간단하다고 생각하니?Vamos 실험 e aprender 알고리즘.

    Limitar chamadas paralelas 회사


    로고 de cara,vamos usar 발전기.Sei queum recurso do JavaScript que muitos desenvolvedores(유럽연합 포함) n ãusam com frequeuência,mas neste caso,isso reduzir á uso de memó ria e criar á digo mais limpo.

    범례


    Vamos definir um problema hipotético primeiro.Temos 100 URL que queremos buscar,mas não queremos mais do que 10 chamadas paralelas ao mesmo tempo.Vamos usar o Google porque eles geralmente conseguem lidar com esse tipo de carga com facilidade!
    // O array de URLs que queremos buscar
    const urls = []
    for (let i = 0; i < 100; i++) {
        // O parâmetro de pesquisa 'q' é o número do índice
        urls.push(`https://www.google.com/search?q=${i}`)
    }
    
    // A requisição é feita em uma função map assíncrona
    async function mapFn(url, i) {
        // Estamos usando https://www.npmjs.com/package/got
        const contents = await got(url)
        return { i, url, contents }
    }
    
    Agora vamos escrever um programa que pegue essas 100 URL,e as mapeia para imprimar os resultados:
    async function main() {
        const results = await mapAllSettled(urls, mapFn, 10)
        console.dir(results)
    }
    
    // Rodando a função "async main()" usando https://www.npmjs.com/package/am
    am(main)
    
    Agora precisamos escrever a funãomapAllSettled()queébem semelhante a,Promise.allSettled(array.map(asyncMapFn)),mas com um limite.Sua assinatura se parece com isso:async function mapAllSettled(array, mapFn, limit).
    Mas vamos voltar um pouco e ver como ser á essa execu ã o. Para simplificar, digamos que temos 10개 URL.Se fossemos buscar todas elas de uma vez,teríamos algo assim:

    Mas se tivéssemos um limite de quatro buscas ao mesmo tempo,seria assim:

    Assim que uma busca for concluda,prosseguiremos com a próxima.카다웨이스라는 사람, 테모스 콰트로 부스카 엠 앤더멘토라는 사람.Vamos regorganizar o tempo de execuão em quatro linhas que serão executadas por alguns“trabalhadores”:

    Todos os trabalhadores“consomem”o mesmo array,mas“inserem”o resultado na posião correta no array resultant,de forma que o valor mapeado para a URL número sete termine na posião sete do array resultate.
    Éaquique os geradores sãoúteis.Podemos definir um gerador que recebe um array eyieldo quea função de mapa espera:
    function* arrayGenerator(array) {
        for (let index = 0; index < array.length; index++) {
            const currentValue = array[index]
            yield [ currentValue, index, array ]
        }
    }
    
    Para manter o formato de saída consistente com oPromise.allSettled(),podemos executar as funões do mapa em um blocotry..catche emitter o resultado em um objeto com o formato:
    async function mapItem(mapFn, currentValue, index, array) {
        try {
            return {
                status: 'fulfilled',
                value: await mapFn(currentValue, index, array)
            }
        } catch (reason) {
            return {
                status: 'rejected',
                reason
            }
        }
    }
    
    Cada trabalhador usa a funão do gerador para buscar ocurrentItem,indexe uma referência aoarray,então chamamosmapItem()para executar omapFn()assíncrono:
    async function worker(id, gen, mapFn, result) {
        for (let [ currentValue, index, array ] of gen) {
            console.time(`Worker ${id} --- index ${index} item ${currentValue}`)
            result[index] = await mapItem(mapFn, currentValue, index, array)
            console.timeEnd(`Worker ${id} --- index ${index} item ${currentValue}`)
        }
    }
    
    Eu adicionei algunsconsole.time()econsole.timeEnd()para tornar a saída mais compreensível,mas,basicamente,essa funão tem duas linhas de código:
  • O loopfor..ofconsome dados do gerador
  • omapItem()chama a funão specificada pelo usuáriomapFn()e returna seus resultados em um objeto que tem o mesmo formato quePromise.allSettled()
  • Agora vamos escrever omapAllSettled()que basicamente cria esses trabalhadores e espera que eles terminem,depos returna os resultados:
    async function mapAllSettled(arr, mapFn, limit = arr.length) {
        const result = []
    
        if (arr.length === 0) {
            return result
        }
    
        const gen = arrayGenerator(arr)
    
        limit = Math.min(limit, arr.length)
    
        const workers = new Array(limit)
        for (let i = 0; i < limit; i++) {
            workers.push(worker(i, gen, mapFn, result))
        }
    
        await Promise.all(workers)
    
        return result
    }
    
    A chave aquiécompatilhar o gerador(gen)entre os trabalhadores.Obviamente,não hásenido em processar se o array estiver vazio,então tiramos esse caso extremo da linha quatro.Além disso,não faz sentido ter mais trabalhadores do que elementos do array,portanto,na linha 10,garantimos quelimiténo máximo igual ao comprimento da matriz.

    결론


    OlimitpadrãO comprimento do array,O que faz com quemapAllSettled()se comporte exatamente comoPromise.allSettled()porque todas as funões do mapa serãO executadas em paralelo.Mas o objetivo dessa funão dar aos usuários o controle para definir um número menor de paralelismo.
    O código completo estáno Githubse vocêquiser brincar com ele(licença MIT).
    Obrigado pela leitura.Se vocêtiver comentários ou perguntas,entre em contato no.

    Créditos 회사


  • async map with limited parallelism in Node.js,escrito originalmente por.
  • 좋은 웹페이지 즐겨찾기