JavaScript递归返回从未解决的承诺

时间:2018-12-04 07:55:52

标签: javascript recursion async-await

在JavaScript中,我有一个对象数组要执行的一些任务。我使用for的{​​{1}}循环遍历此数组,调用函数await并返回doOneTask

只要Promise中的代码按预期工作,效果就很好。但是,这些事情经常失败。再次尝试几乎总是有用。因此,我想在JavaScript代码中实现自动重试的过程。 我的想法是一个递归函数:如果失败,doOneTask会自行调用直到承诺最终解决为止。

我的代码如下:

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()控制台日志后停止。我从不返回循环,循环中的下一个项目也从不执行。

2 个答案:

答案 0 :(得分:1)

您没有使用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

答案 1 :(得分:0)

完成问题之后,但在发布之前,我想到了一些事情:在上面的设置中,当我终于成功时,我并没有解析完全相同的promise对象,但是对于每个函数调用一个新的promise对象生成。我的解决方案/解决方法非常简单:使用q Promise库,将Promise从一个函数调用传递到下一个函数调用:

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;
}

这样,我们确保在第20次执行后的最终事件中解决在promise的第一次调用中生成的同一doOneTask对象。