Разрешить список обещаний Javascript - по очереди? - PullRequest
1 голос
/ 28 сентября 2019

Мой настоящий код очень сложен, но я упросту это настолько, насколько смогу:

let counter = 0
console.log("time counter: ", counter)

setInterval(() => {
    counter = counter + 1;
    console.log("time counter: ", counter)
}, 1000)

const myPromises =
  [ new Promise((resolve, reject) => setTimeout(() => {
                console.log("reject(1)")
                reject(1)
        }, 5 * 1000)) // after 5 seconds.

    , new Promise(resolve => setTimeout(() => {
                console.log("resolve(2)")
                resolve(2)
        }, 3 * 1000)) // after 3 seconds.

    , new Promise(resolve => setTimeout(() => {
                console.log("resolve(3)")
                resolve(3)
        }, 3 * 1000))   // after 3 seconds.

    , new Promise((resolve, reject) => setTimeout(() => {
                console.log("reject(4)")
                reject(4)
        }, 1 * 1000))   // after 1 second.

  ]

async function testIt(){
    const results = myPromises.map(async promise => {
            return new Promise((resolve) => {
                // no matter what happens with the individual promise itself, we resolve.
                promise
                    .then(ok => {
                        resolve({ wasSuccessful: true, result: ok })
                    })
                    .catch(err => {
                        resolve({ wasSuccessful: false, error: err })
                    })
            })
    })

    // should be no need to catch anything here. use await.
    const results_ = await Promise.all(results)

    console.log("results_: ", results_)
}

testIt()
    .catch(err => console.log("this error isn't supposed to happen error: ", err))

Я, по сути, хочу следующее:

1. start the first promise( myPromises[0] ). Wait 5 seconds. After that reject it.

2. start the next promise( myPromises[1] ). Wait 3 seconds. Resolve it.

В этот момент у нас 8 секунд на счетчике.

3. start the next promise( myPromises[2] ). Wait another 3 seconds. Resolve it.

В этот момент у нас 8 + 3 = 11 секунд на счетчике.

4. start the next promise ( myPromises[3] ).. Wait for 1 second.. resolve it.

Полагаю, у вас есть идея ... Как это сделать сейчас?

Заметьте, это не then().then().then() .. я не уменьшаю/ Накапливая этот список , как я видел в других вопросах по этой теме.Я не хочу, чтобы это было отклонено по любой причине.

Вместо этого я хочу получить список результатов.Вот так:

results_:  [
  { wasSuccessful: false, error: 1 },
  { wasSuccessful: true, result: 2 },
  { wasSuccessful: true, result: 3 },
  { wasSuccessful: false, error: 4 }
]

Но обратите внимание на мой вывод console.log .. даже если я получаю правильный результат , он показывает реальный порядок выполнения:

time counter:  0
time counter:  1
resolve(4)
time counter:  2
resolve(2)
resolve(3)
time counter:  3
time counter:  4
reject(1)
results_:  [
  { wasSuccessful: false, error: 1 },   // note the array ordering is correct. rejected,
  { wasSuccessful: true, result: 2 },    // resolved,
  { wasSuccessful: true, result: 3 },   // resolved,
  { wasSuccessful: false, error: 4 }    // rejected. good.
]
time counter:  5
time counter:  6
time counter:  7

По сути, эти обещания были выполнены в параллельном режиме, и какой бы тайм-аут был быстрее, он решается быстрее.

Вместо этого я хотел, чтобы это было так:

time counter:  0
time counter:  1
time counter:  2
time counter:  3
time counter:  4
time counter:  5
reject(1)
time counter:  6
time counter:  7
time counter:  8
resolve(2)
time counter:  9
time counter:  10
time counter:  11
resolve(3)
time counter:  12
resolve(4)
results_:  [
  { wasSuccessful: false, error: 1 },
  { wasSuccessful: true, result: 2 },
  { wasSuccessful: true, result: 3 },
  { wasSuccessful: false, error: 4 }
]
time counter:  13
time counter:  14
...

Это упрощениеНа практике у меня есть список из 30 тыс. Записей, над которыми мне нужно выполнить какое-то действие API и по существу выполнить обещание.Я сгруппировал этот список в подсписки по 10 элементов в каждом.Я буду запускать параллельно каждый подсписок.

Но большой список ... он же списки списков ... нуждается в последовательности:

bigList = [ [ small parallel list 0 ], [ small parallel list 1 ] .. ]

Каждое обещание в этих параллельных очень вычислительноинтенсивно уже.Мне повезло, если я смогу запустить 10 параллельно.Вот почему большой список должен быть последовательным.Или же он запустит дерево обещаний с 30 тыс. Листов, что приведет к сбою.

Все еще не уверен, реалистичен ли этот масштаб, но после того, как я реализую эту последовательность, я смогу точно сказать.

Так как выполнить эти 4 обещания в последовательности? Спасибо.

Ответы [ 3 ]

1 голос
/ 28 сентября 2019

Просто какой-то псевдокод того, какие у вас проблемы.fn - это действие, которое вы собираетесь выполнить, оно должно вернуть Обещание.

async function run_in_chunks(fn, records, chunk_size) {
    for (let chunk of chunkify(records, chunk_size))
       await run_parallel(fn, chunk)
}

async function run_parallel(fn, records) {
     await Promise.all(records.map(r => run_and_handle_error(fn, r)))
}

async function run_and_handle_error(fn, record) {
     try {
          await fn(record)
          log_success(record)
     } catch(e) {
          log_error(record)
     }
}
1 голос
/ 28 сентября 2019

Существующие ответы обеспечивают «ручное» решение, которое вы можете использовать.Но для вашего случая я думаю, что этот метод даст вам все, что вам нужно: https://caolan.github.io/async/v3/docs.html#parallelLimit

Вы даете ему список из 30 тыс. Записей и «предел» - т.е. 10, и это означает, что он всегда выполняет 10 задач (один разпо окончании запускается еще один).

Если вам нужно использовать обещания, помните, что как только вы создаете обещание, оно начинает выполняться в фоновом режиме, и вы не можете его контролировать, поэтому вам нужно иметь какую-то "фабрику"метод.Это означает, что метод при выполнении создает Обещание.

1 голос
/ 28 сентября 2019

Все эти обещания начинаются в одно и то же время, поэтому вы выполняете их параллельно

Вы можете просто включить функцию executor для обещания в массиве - затем запустить исполнителя в reduce вместо map

let counter = 0
console.log("time counter: ", counter)
let int = setInterval(() => {
    counter = counter + 1;
    console.log("time counter: ", counter)
}, 1000)
setTimeout(() => clearInterval(int), 15000);
const myPromiseExecutors =
  [ (resolve, reject) => setTimeout(() => {
                console.log("reject(1)")
                reject(1)
        }, 5 * 1000) // after 5 seconds.

    , resolve => setTimeout(() => {
                console.log("resolve(2)")
                resolve(2)
        }, 3 * 1000) // after 3 seconds.

    , resolve => setTimeout(() => {
                console.log("resolve(3)")
                resolve(3)
        }, 3 * 1000)   // after 3 seconds.

    , (resolve, reject) => setTimeout(() => {
                console.log("reject(4)")
                reject(4)
        }, 1 * 1000)   // after 1 second.

  ]

async function testIt(){
    const results = await myPromiseExecutors.reduce(async (promise, exec) => {
        const ret = await promise;
        try {
            const ok = await new Promise(exec);
            ret.push({ wasSuccessful: true, result: ok });
        } catch(err) {
            ret.push({ wasSuccessful: false, error: err });
        }
        return ret;
    }, Promise.resolve([]))


    console.log("results_: ", results)
}
testIt();

Если честно, вероятно, будет чище использовать for...of loop

let counter = 0
console.log("time counter: ", counter)
let int = setInterval(() => {
    counter = counter + 1;
    console.log("time counter: ", counter)
}, 1000)
setTimeout(() => clearInterval(int), 15000);
const myPromiseExecutors =
  [ (resolve, reject) => setTimeout(() => {
                console.log("reject(1)")
                reject(1)
        }, 5 * 1000) // after 5 seconds.

    , resolve => setTimeout(() => {
                console.log("resolve(2)")
                resolve(2)
        }, 3 * 1000) // after 3 seconds.

    , resolve => setTimeout(() => {
                console.log("resolve(3)")
                resolve(3)
        }, 3 * 1000)   // after 3 seconds.

    , (resolve, reject) => setTimeout(() => {
                console.log("reject(4)")
                reject(4)
        }, 1 * 1000)   // after 1 second.

  ]

async function testIt(){
    const results = [];
    const p = Promise.resolve();
    for (let exec of myPromiseExecutors) {
        try {
            const ok = await new Promise(exec);
            results.push({ wasSuccessful: true, result: ok });
        } catch(err) {
            results.push({ wasSuccessful: false, error: err });
        }
    }

    console.log("results_: ", results)
}
testIt();
...