ES6のPromise.all()を使用するときに同時実行を制限するための最良の方法は何ですか?


98

データベースからクエリされたリストを反復処理し、そのリストの各要素に対してHTTPリクエストを行うコードがいくつかあります。そのリストは、かなり大きな数(数千)になる場合があります。数千の同時HTTP要求でWebサーバーにアクセスしないようにしたいと思います。

このコードの短縮バージョンは現在、次のようになっています...

function getCounts() {
  return users.map(user => {
    return new Promise(resolve => {
      remoteServer.getCount(user) // makes an HTTP request
      .then(() => {
        /* snip */
        resolve();
      });
    });
  });
}

Promise.all(getCounts()).then(() => { /* snip */});

このコードはノード4.3.2で実行されています。繰り返しにPromise.allなりますが、特定の数のPromiseのみが常に進行するように管理できますか?



3
それPromise.allが約束の進行を管理することを忘れないでください-約束はそれ自体を行い、Promise.allただそれらを待ちます。
ベルギ2016年


回答:


51

Promise.all()約束をトリガーして作業を開始するのではなく、約束を作成すること自体がトリガーすることに注意してください。

そのことを念頭に置いて、1つの解決策は、Promiseが解決されるたびに、新しいPromiseを開始する必要があるかどうか、またはすでに制限に達しているかどうかを確認することです。

ただし、ここで車輪の再発明を行う必要はありません。この目的で使用できるライブラリの1つはes6-promise-poolです。彼らの例から:

// On the Web, leave out this line and use the script tag above instead. 
var PromisePool = require('es6-promise-pool')

var promiseProducer = function () {
  // Your code goes here. 
  // If there is work left to be done, return the next work item as a promise. 
  // Otherwise, return null to indicate that all promises have been created. 
  // Scroll down for an example. 
}

// The number of promises to process simultaneously. 
var concurrency = 3

// Create a pool. 
var pool = new PromisePool(promiseProducer, concurrency)

// Start the pool. 
var poolPromise = pool.start()

// Wait for the pool to settle. 
poolPromise.then(function () {
  console.log('All promises fulfilled')
}, function (error) {
  console.log('Some promise rejected: ' + error.message)
})

25
es6-promise-poolがPromiseを使用する代わりに再発明するのは残念です。代わりに、この簡潔なソリューションをお勧めします(ES6またはES7を既に使用している場合)github.com/rxaviers/async-pool
Rafael Xavier

3
両方を見てみると、async-poolの方がはるかに良く見えます!より簡単で軽量です。
エンドレス

2
また、p-limitが最も単純な実装であることがわかりました。以下の私の例を参照してください。stackoverflow.com/a/52262024/8177355
Matthew Rideout 2018

2
tiny-asyc-poolは、約束の同時実行を制限するためのはるかに優れた、邪魔にならない、かなり自然なソリューションだと思います。
サニータンビ

73

P-Limit

promiseの同時実行制限を、カスタムスクリプト、bluebird、es6-promise-pool、およびp-limitと比較しました。p-limitは、このニーズに対応するために最も単純で簡素化された実装を備えていると思います。彼らのドキュメントを参照してください

要件

例の非同期と互換性があるため

私の例

この例では、配列内のすべてのURLに対して関数を実行する必要があります(たとえば、APIリクエストなど)。ここではこれをと呼びfetchData()ます。処理するアイテムが何千もある場合、同時実行性はCPUとメモリのリソースを節約するのに間違いなく役立ちます。

const pLimit = require('p-limit');

// Example Concurrency of 3 promise at once
const limit = pLimit(3);

let urls = [
    "http://www.exampleone.com/",
    "http://www.exampletwo.com/",
    "http://www.examplethree.com/",
    "http://www.examplefour.com/",
]

// Create an array of our promises using map (fetchData() returns a promise)
let promises = urls.map(url => {

    // wrap the function we are calling in the limit function we defined above
    return limit(() => fetchData(url));
});

(async () => {
    // Only three promises are run at once (as defined above)
    const result = await Promise.all(promises);
    console.log(result);
})();

コンソールログの結果は、解決されたPromise応答データの配列です。


4
これをありがとう!これははるかに簡単です
John

3
これは、同時リクエストを制限するために私が見た中で断然最高のライブラリでした。そして素晴らしい例、ありがとう!
ChrisLivdahl19年

2
比較していただきありがとうございます。github.com/rxaviers/async-poolと比較しましたか?
ahong

1
使いやすく、素晴らしい選択。
drmrbrewer

22

使用する Array.prototype.splice

while (funcs.length) {
  // 100 at at time
  await Promise.all( funcs.splice(0, 100).map(f => f()) )
}

2
これは過小評価されているソリューションです。シンプルさが大好きです。
ブラノン

8
これは、プールではなくバッチで関数を実行します。プールでは、別の関数が終了するとすぐに1つの関数が呼び出されます。
cltsang

このソリューションが気に入りました!
prasun

たとえば、プールではなくバッチをビーイングするなど、コンテキストが不足している状態で何をしているのかを把握するのに1秒かかりました。最初または途中でスプライスするたびに、配列を並べ替えています。(ブラウザはすべてのアイテムのインデックスを再作成する必要があります)理論上のパフォーマンスの向上はarr.splice(-100)、順序がまとまらない場合は代わりに最後からものを取得することです。配列を逆にすることができます:P
Endless

バッチで実行するのに非常に便利です。注:次のバッチは、現在のバッチが100%完了するまで開始されません。
ケーシードウェイン

20

イテレータがどのように機能し、どのように消費されるかを知っている場合は、独自の並行性を自分で構築することが非常に簡単になる可能性があるため、追加のライブラリは必要ありません。実演させてください:

/* [Symbol.iterator]() is equivalent to .values()
const iterator = [1,2,3][Symbol.iterator]() */
const iterator = [1,2,3].values()


// loop over all items with for..of
for (const x of iterator) {
  console.log('x:', x)
  
  // notices how this loop continues the same iterator
  // and consumes the rest of the iterator, making the
  // outer loop not logging any more x's
  for (const y of iterator) {
    console.log('y:', y)
  }
}

同じイテレータを使用して、ワーカー間で共有できます。

.entries()代わりに使用した場合は.values()、2D配列を取得します。これを使用[[index, value]]して、2の同時実行数で以下に示します。

const sleep = t => new Promise(rs => setTimeout(rs, t))

async function doWork(iterator) {
  for (let [index, item] of iterator) {
    await sleep(1000)
    console.log(index + ': ' + item)
  }
}

const iterator = Array.from('abcdefghij').entries()
const workers = new Array(2).fill(iterator).map(doWork)
//    ^--- starts two workers sharing the same iterator

Promise.allSettled(workers).then(() => console.log('done'))

これの利点は、すべてを一度に準備する代わりに、ジェネレーター機能を使用できることです。


注:これと例のasync-poolとの違いは、2つのワーカーを生成することです。したがって、1つのワーカーが何らかの理由でインデックス5でエラーをスローしても、他のワーカーが残りの作業を停止することはありません。したがって、2つの同時実行を1に減らします(そこで停止することはありません)。したがって、doWork関数内のすべてのエラーをキャッチすることをお勧めします。


これはすごい!エンドレスありがとう!
user34137 2319

これは間違いなくクールなアプローチです!並行性がタスクリストの長さを超えないように注意してください(とにかく結果を気にする場合)。余分なものが発生する可能性があります。
Kris Oye

後でもっとクールになるかもしれないのは、StreamsがReadable.from(iterator)サポートを取得したときです。Chromeはすでにストリームを転送可能にしています。したがって、読み取り可能なストリームを作成してWebワーカーに送信すると、それらすべてが同じ基盤となるイテレーターを使用することになります。
エンドレス

16

bluebirdのPromise.mapは、並行実行オプションを使用して、並行して実行するプロミスの数を制御できます。.allpromise配列を作成する必要がないためよりも簡単な場合があります。

const Promise = require('bluebird')

function getCounts() {
  return Promise.map(users, user => {
    return new Promise(resolve => {
      remoteServer.getCount(user) // makes an HTTP request
      .then(() => {
        /* snip */
        resolve();
       });
    });
  }, {concurrency: 10}); // <---- at most 10 http requests at a time
}

bluebirdは、より速い約束が必要な場合は感謝し、1つの目的にのみ使用する場合は最大18kbの追加のジャンクです;)
エンドレス

1
すべては、1つのことがあなたにとってどれほど重要であるか、そして他のより速く/より簡単なより良い方法があるかどうかに依存します。典型的なトレードオフ。使いやすさと機能を数kb以上選択しますが、YMMVです。
Jingshao Chen 2018

11

httpリクエストを制限するためにpromiseを使用する代わりに、ノードの組み込みhttp.Agent.maxSocketsを使用します。これにより、ライブラリを使用したり、独自のプーリングコードを記述したりする必要がなくなり、制限する内容をより細かく制御できるという利点が追加されます。

agent.maxSockets

デフォルトでは、Infinityに設定されています。エージェントがオリジンごとに開くことができる同時ソケットの数を決定します。オリジンは、「host:port」または「host:port:localAddress」の組み合わせです。

例えば:

var http = require('http');
var agent = new http.Agent({maxSockets: 5}); // 5 concurrent connections per origin
var request = http.request({..., agent: agent}, ...);

同じオリジンに対して複数のリクエストを行う場合は、keepAlivetrueに設定すると便利な場合があります(詳細については、上記のドキュメントを参照してください)。


11
それでも、数千のクロージャをすぐに作成し、ソケットをプールすることはあまり効率的ではないようです。
ベルギ2016年

3

ライブラリasync-poolをお勧めします:https//github.com/rxaviers/async-pool

npm install tiny-async-pool

説明:

ネイティブES6 / ES7を使用して、同時実行が制限された複数のpromise-returning&async関数を実行します

asyncPoolは、限られた同時実行プールで複数のpromise-returningおよびasync関数を実行します。約束の1つが拒​​否するとすぐに拒否します。すべての約束が完了すると解決します。イテレータ関数をできるだけ早く呼び出します(同時実行制限の下で)。

使用法:

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]`.

1
私のために働きます。ありがとう。これは素晴らしい図書館です。
サニータンビ

2

再帰を使用して解決できます。

アイデアは、最初に許可された最大数のリクエストを送信し、これらの各リクエストは完了時に再帰的に送信し続ける必要があるということです。

function batchFetch(urls, concurrentRequestsLimit) {
    return new Promise(resolve => {
        var documents = [];
        var index = 0;

        function recursiveFetch() {
            if (index === urls.length) {
                return;
            }
            fetch(urls[index++]).then(r => {
                documents.push(r.text());
                if (documents.length === urls.length) {
                    resolve(documents);
                } else {
                    recursiveFetch();
                }
            });
        }

        for (var i = 0; i < concurrentRequestsLimit; i++) {
            recursiveFetch();
        }
    });
}

var sources = [
    'http://www.example_1.com/',
    'http://www.example_2.com/',
    'http://www.example_3.com/',
    ...
    'http://www.example_100.com/'
];
batchFetch(sources, 5).then(documents => {
   console.log(documents);
});

2

これは、コピーアンドペーストに適した、完全Promise.all()/map()代替機能を備えた、同時実行制限のあるES7ソリューションです。

これと同様にPromise.all()、戻り順序と、約束されていない戻り値のフォールバックを維持します。

また、他のソリューションのいくつかが見逃しているいくつかの側面を示しているため、さまざまな実装の比較も含めました。

使用法

const asyncFn = delay => new Promise(resolve => setTimeout(() => resolve(), delay));
const args = [30, 20, 15, 10];
await asyncPool(args, arg => asyncFn(arg), 4); // concurrency limit of 4

実装

async function asyncBatch(args, fn, limit = 8) {
  // Copy arguments to avoid side effects
  args = [...args];
  const outs = [];
  while (args.length) {
    const batch = args.splice(0, limit);
    const out = await Promise.all(batch.map(fn));
    outs.push(...out);
  }
  return outs;
}

async function asyncPool(args, fn, limit = 8) {
  return new Promise((resolve) => {
    // Copy arguments to avoid side effect, reverse queue as
    // pop is faster than shift
    const argQueue = [...args].reverse();
    let count = 0;
    const outs = [];
    const pollNext = () => {
      if (argQueue.length === 0 && count === 0) {
        resolve(outs);
      } else {
        while (count < limit && argQueue.length) {
          const index = args.length - argQueue.length;
          const arg = argQueue.pop();
          count += 1;
          const out = fn(arg);
          const processOut = (out, index) => {
            outs[index] = out;
            count -= 1;
            pollNext();
          };
          if (typeof out === 'object' && out.then) {
            out.then(out => processOut(out, index));
          } else {
            processOut(out, index);
          }
        }
      }
    };
    pollNext();
  });
}

比較

// A simple async function that returns after the given delay
// and prints its value to allow us to determine the response order
const asyncFn = delay => new Promise(resolve => setTimeout(() => {
  console.log(delay);
  resolve(delay);
}, delay));

// List of arguments to the asyncFn function
const args = [30, 20, 15, 10];

// As a comparison of the different implementations, a low concurrency
// limit of 2 is used in order to highlight the performance differences.
// If a limit greater than or equal to args.length is used the results
// would be identical.

// Vanilla Promise.all/map combo
const out1 = await Promise.all(args.map(arg => asyncFn(arg)));
// prints: 10, 15, 20, 30
// total time: 30ms

// Pooled implementation
const out2 = await asyncPool(args, arg => asyncFn(arg), 2);
// prints: 20, 30, 15, 10
// total time: 40ms

// Batched implementation
const out3 = await asyncBatch(args, arg => asyncFn(arg), 2);
// prints: 20, 30, 20, 30
// total time: 45ms

console.log(out1, out2, out3); // prints: [30, 20, 15, 10] x 3

// Conclusion: Execution order and performance is different,
// but return order is still identical

結論

asyncPool() 以前のリクエストが終了するとすぐに新しいリクエストを開始できるため、最適なソリューションである必要があります。

asyncBatch() 実装が理解しやすいため、比較として含まれていますが、次のバッチを開始するために同じバッチ内のすべての要求を終了する必要があるため、パフォーマンスが低下するはずです。

この不自然な例では、無制限のバニラPromise.all()がもちろん最速ですが、他のバニラは実際の混雑シナリオでより望ましいパフォーマンスを発揮する可能性があります。

更新

他の人がすでに提案している非同期プールライブラリは、ほぼ同じように機能し、Promise.race()の巧妙な使用法でより簡潔な実装を備えているため、おそらく私の実装のより良い代替手段です:https//github.com/rxaviers/ async-pool / blob / master / lib / es7.js

うまくいけば、私の答えはまだ教育的価値を提供することができます。


1

ストリーミングと「p-limit」の基本的な例を次に示します。http読み取りストリームをmongodbにストリーミングします。

const stream = require('stream');
const util = require('util');
const pLimit = require('p-limit');
const es = require('event-stream');
const streamToMongoDB = require('stream-to-mongo-db').streamToMongoDB;


const pipeline = util.promisify(stream.pipeline)

const outputDBConfig = {
    dbURL: 'yr-db-url',
    collection: 'some-collection'
};
const limit = pLimit(3);

async yrAsyncStreamingFunction(readStream) => {
        const mongoWriteStream = streamToMongoDB(outputDBConfig);
        const mapperStream = es.map((data, done) => {
                let someDataPromise = limit(() => yr_async_call_to_somewhere())

                    someDataPromise.then(
                        function handleResolve(someData) {

                            data.someData = someData;    
                            done(null, data);
                        },
                        function handleError(error) {
                            done(error)
                        }
                    );
                })

            await pipeline(
                readStream,
                JSONStream.parse('*'),
                mapperStream,
                mongoWriteStream
            );
        }

0

だから私はいくつかの例を私のコードで機能させるように試みましたが、これはインポートスクリプトのみであり、本番コードではないため、npmパッケージのbatch-promisesを使用するのが確かに私にとって最も簡単なパスでした

注:Promiseをサポートするか、ポリフィルするにはランタイムが必要です。

Api batchPromises(int:batchSize、array:Collection、i => Promise:Iteratee)Promise:Iterateeは、各バッチの後に呼び出されます。

使用する:

batch-promises
Easily batch promises

NOTE: Requires runtime to support Promise or to be polyfilled.

Api
batchPromises(int: batchSize, array: Collection, i => Promise: Iteratee)
The Promise: Iteratee will be called after each batch.

Use:
import batchPromises from 'batch-promises';
 
batchPromises(2, [1,2,3,4,5], i => new Promise((resolve, reject) => {
 
  // The iteratee will fire after each batch resulting in the following behaviour:
  // @ 100ms resolve items 1 and 2 (first batch of 2)
  // @ 200ms resolve items 3 and 4 (second batch of 2)
  // @ 300ms resolve remaining item 5 (last remaining batch)
  setTimeout(() => {
    resolve(i);
  }, 100);
}))
.then(results => {
  console.log(results); // [1,2,3,4,5]
});


0

外部ライブラリを使用したくない場合は、再帰が答えです

downloadAll(someArrayWithData){
  var self = this;

  var tracker = function(next){
    return self.someExpensiveRequest(someArrayWithData[next])
    .then(function(){
      next++;//This updates the next in the tracker function parameter
      if(next < someArrayWithData.length){//Did I finish processing all my data?
        return tracker(next);//Go to the next promise
      }
    });
  }

  return tracker(0); 
}

0

これは私がPromise.raceここのコード内で使用したことです

const identifyTransactions = async function() {
  let promises = []
  let concurrency = 0
  for (let tx of this.transactions) {
    if (concurrency > 4)
      await Promise.race(promises).then(r => { promises = []; concurrency = 0 })
    promises.push(tx.identifyTransaction())
    concurrency++
  }
  if (promises.length > 0)
    await Promise.race(promises) //resolve the rest
}

例を見たい場合:https//jsfiddle.net/thecodermarcelo/av2tp83o/5/


2
私はその並行性とは呼びません...それはバッチ実行のようなものです...あなたは4つのタスクを実行し、すべてが終了するのを待ってから次の4つを実行します。 、使用する必要があるのはPromise.race
エンドレス


0
  • @tcoocの答えはとてもクールでした。それについて知りませんでした、そして将来それを活用するでしょう。
  • @MatthewRideoutの答えも楽しかったですが、外部ライブラリを使用しています!!

可能な限り、図書館に行くのではなく、自分でこの種のものを開発することを試みます。あなたは、以前は気が遠くなるように思えた多くの概念を学ぶことになります。

あなたたちはこの試みについてどう思いますか:
私はそれをよく考えて、それは機能していると思いますが、そうでないか、根本的に何かが間違っているかどうかを指摘してください)

 class Pool{
        constructor(maxAsync) {
            this.maxAsync = maxAsync;
            this.asyncOperationsQueue = [];
            this.currentAsyncOperations = 0
        }

        runAnother() {
            if (this.asyncOperationsQueue.length > 0 && this.currentAsyncOperations < this.maxAsync) {
                this.currentAsyncOperations += 1;
                this.asyncOperationsQueue.pop()()
                    .then(() => { this.currentAsyncOperations -= 1; this.runAnother() }, () => { this.currentAsyncOperations -= 1; this.runAnother() })
            }
        }

        add(f){  // the argument f is a function of signature () => Promise
            this.runAnother();
            return new Promise((resolve, reject) => {
                this.asyncOperationsQueue.push(
                    () => f().then(resolve).catch(reject)
                )
            })
        }
    }

//#######################################################
//                        TESTS
//#######################################################

function dbCall(id, timeout, fail) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (fail) {
               reject(`Error for id ${id}`);
            } else {
                resolve(id);
            }
        }, timeout)
    }
    )
}


const dbQuery1 = () => dbCall(1, 5000, false);
const dbQuery2 = () => dbCall(2, 5000, false);
const dbQuery3 = () => dbCall(3, 5000, false);
const dbQuery4 = () => dbCall(4, 5000, true);
const dbQuery5 = () => dbCall(5, 5000, false);


const cappedPool = new Pool(2);

const dbQuery1Res = cappedPool.add(dbQuery1).catch(i => i).then(i => console.log(`Resolved: ${i}`))
const dbQuery2Res = cappedPool.add(dbQuery2).catch(i => i).then(i => console.log(`Resolved: ${i}`))
const dbQuery3Res = cappedPool.add(dbQuery3).catch(i => i).then(i => console.log(`Resolved: ${i}`))
const dbQuery4Res = cappedPool.add(dbQuery4).catch(i => i).then(i => console.log(`Resolved: ${i}`))
const dbQuery5Res = cappedPool.add(dbQuery5).catch(i => i).then(i => console.log(`Resolved: ${i}`))

このアプローチは、scala / javaのスレッドプールに似た優れたAPIを提供します。
でプールのインスタンスを1つ作成した後const cappedPool = new Pool(2)、単純にでプールにpromiseを提供しますcappedPool.add(() => myPromise)
明らかに、約束がすぐに開始されないようにする必要があります。そのため、関数を使用して「怠惰に提供」する必要があります。

最も重要なことは、メソッドの結果がadd Promiseであり、元のPromiseの値で完了/解決されることに注意してください。これにより、非常に直感的に使用できます。

const resultPromise = cappedPool.add( () => dbCall(...))
resultPromise
.then( actualResult => {
   // Do something with the result form the DB
  }
)

0

残念ながら、ネイティブのPromise.allでそれを行う方法はないため、創造性を発揮する必要があります。

これは、外部のライブラリを使用せずに見つけることができる最も迅速で簡潔な方法です。

イテレータと呼ばれる新しいJavaScript機能を利用します。イテレータは基本的に、処理されたアイテムと処理されていないアイテムを追跡します。

コードで使用するには、非同期関数の配列を作成します。各非同期関数は、同じイテレータに、処理する必要のある次の項目を要求します。各関数は独自のアイテムを非同期で処理し、完了するとイテレーターに新しいアイテムを要求します。イテレータがアイテムを使い果たすと、すべての機能が完了します。

インスピレーションを与えてくれた@Endlessに感謝します。

var items = [
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
    "https://www.stackoverflow.com",
];

var concurrency = 5

Array(concurrency).fill(items.entries()).map(async (cursor) => {
    for(let [index, url] of cursor){
        console.log("getting url is ", index, url);
        // run your async task instead of this next line
        var text = await fetch(url).then(res => res.text());
        console.log("text is", text.slice(0,20));
    }
})


なぜこれがマークダウンされたのか興味があります。それは私が思いついたものと非常に似ています。
Kris Oye

0

非常に多くの優れたソリューション。@Endlessによって投稿されたエレガントなソリューションから始めて、外部ライブラリを使用せず、バッチで実行しないこの小さな拡張メソッドで終わりました(ただし、非同期などの機能があることを前提としています)。

Promise.allWithLimit = async (taskList, limit = 5) => {
    const iterator = taskList.entries();
    let results = new Array(taskList.length);
    let workerThreads = new Array(limit).fill(0).map(() => 
        new Promise(async (resolve, reject) => {
            try {
                let entry = iterator.next();
                while (!entry.done) {
                    let [index, promise] = entry.value;
                    try {
                        results[index] = await promise;
                        entry = iterator.next();
                    }
                    catch (err) {
                        results[index] = err;
                    }
                }
                // No more work to do
                resolve(true); 
            }
            catch (err) {
                // This worker is dead
                reject(err);
            }
        }));

    await Promise.all(workerThreads);
    return results;
};

    Promise.allWithLimit = async (taskList, limit = 5) => {
        const iterator = taskList.entries();
        let results = new Array(taskList.length);
        let workerThreads = new Array(limit).fill(0).map(() => 
            new Promise(async (resolve, reject) => {
                try {
                    let entry = iterator.next();
                    while (!entry.done) {
                        let [index, promise] = entry.value;
                        try {
                            results[index] = await promise;
                            entry = iterator.next();
                        }
                        catch (err) {
                            results[index] = err;
                        }
                    }
                    // No more work to do
                    resolve(true); 
                }
                catch (err) {
                    // This worker is dead
                    reject(err);
                }
            }));
    
        await Promise.all(workerThreads);
        return results;
    };

    const demoTasks = new Array(10).fill(0).map((v,i) => new Promise(resolve => {
       let n = (i + 1) * 5;
       setTimeout(() => {
          console.log(`Did nothing for ${n} seconds`);
          resolve(n);
       }, n * 1000);
    }));

    var results = Promise.allWithLimit(demoTasks);

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.