Get Out of Reject promise recursion in Javascript

Viewed 73

Suppose I have this function below:

var a = 0;
function test () {
  return new Promise(function (resolve, reject) {
    a++;
    if (a < 10) {
      test()
      reject(a)
    } else {
      resolve(a)
    }
  })
}

test().then(function (a) {
  console.log('resolve', a)
}).catch(function (a) {
  console.log('reject', a)
})

It prints:

reject 10

I expect that it will print reject 1 ... 10.

How can I achieve this?

2 Answers

The reason you only see the one result is that nothing uses or reports the results of the recursive calls to test, just the first one. You need to move the reporting logic, and the increment logic:

var a = 0;
function test () {
    doOne(a++).then(function (a) {
    //     ^^
      console.log('resolve', a)
      if (a < 10) {
        test(); // Keep going
      }
    }).catch(function (a) {
      console.log('reject', a)
      if (a < 10) {
        test(); // Keep going
      }
    })
}

function doOne (a) {
  return new Promise(function (resolve, reject) {
    if (a < 10) {
      reject(a)
    } else {
      resolve(a)
    }
  })
}

test()

But note that unless you change your if (a < 10) check, it'll show resolve 10 not reject 10 at the end.

It's also worth noting that nothing in your code is asynchronous except the calls to the promise settlement handlers (because those calls are always asynchronous). Promises are used to report the completion and result of asynchronous processes. There's no reason to use them for synchronous processes.

Or here's another approach that avoids using a variable that's global to the code, waits for a previous promise to settle before moving on to the next one (which may or may not be what you want), makes it possible to know when the overall operation is complete, and avoids repeating the a < 10 logic:

function test(a) {
    return doOne(a).then(function (a) {
      console.log('resolve', a)
    }).catch(function (a) {
      console.log('reject', a)
      return test(a + 1);
    });
}

function doOne(a) {
  return new Promise(function (resolve, reject) {
    if (a < 10) {
      reject(a)
    } else {
      resolve(a)
    }
  })
}

test(0)
.finally(() => {
  console.log("Done");
});

Note that that one relies on the fact that we only want to continue when doOne rejects its promise.

It sounds like you should be using generators instead of promises.

function* test(a) {
  while (a < 10) yield ++a;
  return a;
}

var iterator = test(0), val;
while (!(val = iterator.next()).done) console.log(val.value);

You could also just use a for of loop.

function* test(a) {
  while (a < 10) yield ++a;
  return a;
}

for (var val of test(0)) console.log(val);

Related