Ali Zgheib
Ali Zgheib

Reputation: 148

Consecutive promises not resolving

I'm working on a project where I use some promises/timeout to delay the call of different functions.

the code of this project is a bit big, so I thought it might be better to make a sample of it and test it and find the reason that is causing the promise ( only last one ) to not resolve.

After making a sample of this code, the same bug that was happening to me occurred again, so it's when I knew that there's something wrong in my logic and wanted to seek some help:

I'll write the code below and explain it after:

let i = 0; // declaring i which help us recall the test method (until a given length)

//function 1 2 and 3 are 3 functions that take few seconds to resolve ( made it simpler for you guys 
// to understand my problem


function function1() {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 1");
      res();
    }, 1000);
  });
}

function function2() {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 2");
      res();
    }, 1000);
  });
}
//in function 3 if the i didn't reach the length given ( made static here=10) it will finish its code
// then call the test method with the new/incremented i
// this keep happening until i= the required length(10) where the function will return true
// and when the function 3 return back true.. the test function should resolve and call the final()
// function

function function3(i) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 3");
      if (i + 1 == 10) {
        res({ bool: true });
      } else {
        i++;
        res({ bool: false, i: i });
      }
    }, 2000);
  });
}

function final() {
  console.log("final");
}

function test(i) {
  return new Promise((res, rej) => {
    function1().then((data) => {
      function2().then((data) => {
        function3(i).then((data) => {
          console.log("boolean: ", data.bool);
          if (data.bool) {
            console.log("true..so if");
             res();
          } else {
            console.log("false..so else");
            test(data.i);
          }
        });
      });
    });
  }); //end of promise
}

test(i).then(() => {
  final();
});

the code runs well and it prints few times consecutively:
from function 1
from function 2
from function 3
....

and here is where the final function should be called to print "final" but its not. while debugging, I noticed that the function 3 return back false 9 times and true in the last one and this true should cause the test function to resolve but its not.

Upvotes: 0

Views: 61

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370729

Right now, in the case of the else, you have a dangling Promise not connected to anything else:

      } else {
        console.log("false..so else");
        test(data.i);
      }

The currently running test function never has its res called after the recursive call, so it remains unresolved forever, so the initial call of

test(i).then(() => {
  final();
});

never results in final running.

While you could fix it by adding test(data.i).then(res), to ensure the Promises are all connected:

let i = 0; // delaring i wich help us recall the test method (until a given length)

//function 1 2 and 3 are 3 functions that take few seconds to resolve ( made it simpler for you guys 
// to understand my problem


function function1() {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 1");
      res();
    }, 100);
  });
}

function function2() {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 2");
      res();
    }, 100);
  });
}
//in function 3 if the i didnt reach the length given ( made static here=10) it will finish its code
// then call the test method with the new/incremented i
// this keep happening until i= the required length(10) where the function will return true
// and when the function 3 return back true.. the test function should resolve and call the final()
// function

function function3(i) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 3");
      if (i + 1 == 10) {
        res({ bool: true });
      } else {
        i++;
        res({ bool: false, i: i });
      }
    }, 200);
  });
}

function final() {
  console.log("final");
}

function test(i) {
  return new Promise((res, rej) => {
    function1().then((data) => {
      function2().then((data) => {
        function3(i).then((data) => {
          console.log("boolean: ", data.bool);
          if (data.bool) {
            console.log("true..so if");
            return res();
          } else {
            console.log("false..so else");
            test(data.i).then(res);
          }
        });
      });
    });
  }); //end of promise
}

test(i).then(() => {
  final();
});

It would be much better to avoid the explicit Promise construction antipattern, and avoid the Promise-as-callback antipattern. This can be done concisely by making test an async function:

async function test(i) {
  await function1();
  await function2();
  const data = await function3(i);
  console.log("boolean: ", data.bool);
  if (data.bool) {
    console.log("true..so if");
    return;
  } else {
    console.log("false..so else");
    await test(data.i);
  }
}

let i = 0; // delaring i wich help us recall the test method (until a given length)

//function 1 2 and 3 are 3 functions that take few seconds to resolve ( made it simpler for you guys 
// to understand my problem


function function1() {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 1");
      res();
    }, 100);
  });
}

function function2() {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 2");
      res();
    }, 100);
  });
}
//in function 3 if the i didnt reach the length given ( made static here=10) it will finish its code
// then call the test method with the new/incremented i
// this keep happening until i= the required length(10) where the function will return true
// and when the function 3 return back true.. the test function should resolve and call the final()
// function

function function3(i) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 3");
      if (i + 1 == 10) {
        res({ bool: true });
      } else {
        i++;
        res({ bool: false, i: i });
      }
    }, 200);
  });
}

function final() {
  console.log("final");
}

async function test(i) {
  await function1();
  await function2();
  const data = await function3(i);
  console.log("boolean: ", data.bool);
  if (data.bool) {
    console.log("true..so if");
    return;
  } else {
    console.log("false..so else");
    await test(data.i);
  }
}
test(i).then(() => {
  final();
});

Or, if you have to keep using .thens:

function test(i) {
  return function1()
    .then(function2)
    .then(() => function3(i))
    .then((data) => {
      console.log("boolean: ", data.bool);
      if (data.bool) {
        console.log("true..so if");
        return;
      } else {
        console.log("false..so else");
        return test(data.i);
      }
    });
}

let i = 0; // delaring i wich help us recall the test method (until a given length)

//function 1 2 and 3 are 3 functions that take few seconds to resolve ( made it simpler for you guys 
// to understand my problem


function function1() {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 1");
      res();
    }, 100);
  });
}

function function2() {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 2");
      res();
    }, 100);
  });
}
//in function 3 if the i didnt reach the length given ( made static here=10) it will finish its code
// then call the test method with the new/incremented i
// this keep happening until i= the required length(10) where the function will return true
// and when the function 3 return back true.. the test function should resolve and call the final()
// function

function function3(i) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log("from function 3");
      if (i + 1 == 10) {
        res({ bool: true });
      } else {
        i++;
        res({ bool: false, i: i });
      }
    }, 200);
  });
}

function final() {
  console.log("final");
}

function test(i) {
  return function1()
    .then(function2)
    .then(() => function3(i))
    .then((data) => {
      console.log("boolean: ", data.bool);
      if (data.bool) {
        console.log("true..so if");
        return;
      } else {
        console.log("false..so else");
        return test(data.i);
      }
    });
}
test(i).then(() => {
  final();
});

Whenever you have a Promise, you should almost always return it, or be the initial asynchronous operation of the script.

Upvotes: 1

Related Questions