JavaScript-рекурсия, возвращающая обещание, которое никогда не разрешается - PullRequest
0 голосов
/ 04 декабря 2018

В JavaScript у меня есть массив объектов, которые нужно выполнить.Я перебираю этот массив с циклом for с await, вызывая функцию doOneTask с возвратами Promise.

, которая работает довольно хорошо, пока код внутри doOneTask работает какожидается.Однако эти вещи часто терпят неудачу.Попытка повторения помогает почти все время.Итак, я хотел бы реализовать процедуру для автоматической повторной попытки внутри кода JavaScript.Моя идея была рекурсивной функцией: * В случае сбоя doOneTask вызовите себя до выполнения обещания, если оно окончательно разрешено.

Мой код выглядит следующим образом:

var tasks = [{label: 'task0'},{label: 'task1'},{label: 'task2'}];
async function mainFunction() {
    for(let k = 0; k < tasks.length; k++) {
        await doOneTask(tasks[k]);
        console.log("doOneTask done for index " + k);
    }
    console.log("End reached!");
}

function doOneTask(task) {        
    return new Promise(async function (resolve,reject) {    
       console.log("Starting with: " + task.label);            
       let checkIfDoeSomeStuffWorked = await doSomeAsyncStuff();           
       if(checkIfDoeSomeStuffWorked == false) {
           console.log(task.label + ": FAILED");
           return doOneTask(task);
       }
       else {
           console.log(task.label + ": SUCCESS");
           resolve(true);
       }
    }); 
}

function doSomeAsyncStuff() {
    return new Promise(function (resolve,reject) {        
        var myRandom = Math.random();        
        if(myRandom < 0.3) {
            resolve(true);
        }
        else {
            resolve(false);
        }        
    });
}

mainFunction();

(В реальной жизни, doSomeAsyncStuff - это внутренний вызов, который часто не срабатывает. Часть random() предназначена только для демонстрации. На самом деле, я также ограничиваю количество попыток перед остановкой сценария.)

Однако это не такт работа.В случае сбоя сценарий останавливается после достижения журнала консоли SUCCESS.Я никогда не возвращаюсь к циклу, и следующие элементы в цикле никогда не выполняются.

Ответы [ 2 ]

0 голосов
/ 19 мая 2019

Вы не используете библиотеку q.async функции всегда возвращают Promise, так что вы можете немного упростить свой код -

async function doOneTask (task) {
  const result = await doSomeAsyncStuff()
  if (result === false) {
    console.log(`${task} failed`)
    return doOneTask(task)
  }
  else {
    console.log(`${task} passed`)
    return true
  }
}

Ваша ложная функция doSomeAsyncStuff тоже может быть очищена -

async function doSomeAsyncStuff () {
  return Math.random() < 0.3
}

Нодавайте добавим фиктивную задержку в 1 секунду, чтобы мы могли показать, что все работает на 100% -

async function doSomeAsyncStuff () {
  return new Promise(resolve =>
    setTimeout(resolve, 1000, Math.random() < 0.3)
  )
}

Наконец, ваша функция main использует действительно старое соглашение о циклах.Поскольку вы используете современный JavaScript, вы также можете использовать синтаксис for-of -

async function main (tasks = []) {
  for (const t of tasks) {
    await doOneTask(t)
  }
  return "done"
}

Наконец, мы запустим программу -

const tasks =
  [ 'task0', 'task1', 'task2' ]

main(tasks).then(console.log, console.error)
// task0 failed
// task0 passed
// task1 failed
// task1 failed
// task1 passed
// task2 passed
// done

Разверните фрагмент ниже, чтобы проверить результатыв вашем браузере -

async function doOneTask (task) {
  const result = await doSomeAsyncStuff()
  if (result === false) {
    console.log(`${task} failed`)
    return doOneTask(task)
  }
  else {
    console.log(`${task} passed`)
    return true
  }
}

async function doSomeAsyncStuff () {
  return new Promise(resolve =>
    setTimeout(resolve, 1000, Math.random() < 0.3)
  )
}

async function main (tasks = []) {
  for (const t of tasks) {
    await doOneTask(t)
  }
  return "done"
}

const tasks =
  [ 'task0', 'task1', 'task2' ]

main(tasks).then(console.log, console.error)
// task0 failed
// task0 passed
// task1 failed
// task1 failed
// task1 passed
// task2 passed
// done
0 голосов
/ 04 декабря 2018

После завершения вопроса, но непосредственно перед публикацией, мне пришло в голову что-то: в приведенной выше настройке я не разрешаю один и тот же объект обещания, когда я наконец достигаю успеха, но для каждой функции вызываю новый объект обещаниягенерируется.Мое решение / обходной путь довольно прост: используйте q библиотеку обещаний, передающую обещание от одного вызова функции следующему вызову функции:

var q = require('q');
async function doOneTask(task,promiseObj) {

    if(!promiseObj) {
        var promiseObj = q.defer();
    }

    console.log("Starting with: " + task.label); 

    let checkIfDoeSomeStuffWorked = await doSomeAsyncStuff();

    if(checkIfDoeSomeStuffWorked == false) {
           console.log(task.label + ": FAILED");
           return doOneTask(task,promiseObj);
    }
    else {
           console.log(task.label + ": SUCCESS");
           promiseObj.resolve(true);
       }

    return promiseObj.promise;
}

Таким образом, мы гарантируем, что тот же самый promiseобъект, который генерируется при первом вызове doOneTask, разрешается в конечном событии после 20-го выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...