2

UPDATE: I FOUND AN "HOMEMADE" SOLUTION BY MY OWN, SCROLL DOWN TO THIS THIS SOLUTION!

When I'm doing NodeJS promises "manually" I have the option to pass the resolve value from the parent promise to it's child, or more precisely, from the first promise to the second and so for and so on.

So, after first promise is done, I can continue using it's resolved data in the next promise. Sometimes this data is crucial for the whole process. Such as sending a SMS via remote API, if the SMS will not be sent, I cannot be able to manage the next function.

I have tried the "shortcut" (*sorry if it does not meant to be a shortcut): Promise.all. Well it's nice, and it pops up the catched promise reject value first and if everything is okay it goes to finish. But how can I preserve the resolved data in the same manner I have described above?

I will push here some code, so we can understand each other better:

Trying to do this using Promise.all

function test1() {
    return new Promise(function(resolve, reject) {
      resolve("test1");
    });
  }

  function test2() {
    return new Promise(function(resolve, reject) {
      resolve("test2");
    });
  }

  function test3() {
    return new Promise(function(resolve, reject) {
      resolve("test3");
    });
  }

  Promise.all([test1, test2, test3].map(func => func())).then((result) => {
    console.log('result: ', result);
  }).catch((result) => {
    console.log("Catched: ", result);
  });

Trying to do this using the "manual" way:

function test1() {
    return new Promise(function(resolve, reject) {
      resolve("test1");
    });
  }

  function test2(p) {
    return new Promise(function(resolve, reject) {
      resolve("test2");
    });
  }

  function test3(p) {
    return new Promise(function(resolve, reject) {
      resolve("test3");
    });
  }

  test1().then(function(result) {
    return test2(result);
  }).then(function(result) {
    return test3();
  }).then(function(result) {
    console.log("Finished");
  });

Now for the catch section if I am using the Promise.all function with the catch call back, it will rise the first catched match. This is not good. Since I want to first tell the user he forgot filling email, later he has to be notified whether there was a problem sending SMS. But it does not working like that.

Actually, I can do it the manual way seperate for then and seperate for catch, but it is a mess.. And it is very easy to cause mistake in the syntax that will take hours to find.

********************** UPDATE - I FOUND AN HOMEMADE SOLUTIN!!! **********************

function test1() {
    // some process return in overall as boolean
    var overall = true;
    if (overall)
      return test2({test1: "is resolved"});
    return catched("rejected on 1");
  }

  function test2(result) {
    // some process return in overall as boolean
    var overall = true;
    result.test2 = "is resolved";
    if (overall)
      return test3(result);
    return catched("rejected on 2");
  }

  function test3(result) {
    // some process return in overall as boolean
    var overall = true;
    result.test3 = "is resolved";
    if (overall)
      return finished(result);
    return catched("rejected on 3");
  }

  function finished(result) {
    console.log(result);
  }

  function catched(result) {
    console.log(result);
  }

  test1();

test1() will run first. After each of them done it goes to the next, syntax is simple, my homemade Promise.all is super simple thats the solution. Plus, if in future we want to add a middle function between test2 to test3, we should not think about adding it in the end handle.

Hmm.. probably that is it for now. Hope it is clear to understand and help others!

THANKS :)

  • Those code blocks are not equivalent. `Promise.all()` will execute all of the Promises that you give it at the same time, not sequentially. – zero298 Nov 08 '17 at 21:38
  • 1
    Correct me please, what should i use instead? –  Nov 08 '17 at 21:39
  • 1
    That being said, you may want to look into [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). They give a more terse way of doing Promises. – zero298 Nov 08 '17 at 21:40
  • What is the issue with code at Question? – guest271314 Nov 08 '17 at 21:46
  • I have edited, and putted the issue: It is unreadable to concatenate too many functions manually one after one without forget something or redeclare something over again. It is vulnerable to syntax errors. (in real project this file could reach tons of lines). Therefore I need, or I shall define it as, we need, a one shot function or a method doing it for us without the chance of ruin the whole file. –  Nov 09 '17 at 06:44

3 Answers3

5

If your promises are all taking the result of the previous promise and returning to the next (like they do in your example), you can make the syntax much terser by doing something like this:

function test1() {
  return new Promise(function(resolve, reject) {
    resolve("test1");
  });
}

function test2(p) {
  return new Promise(function(resolve, reject) {
    resolve(p + " test2");
  });
}

function test3(p) {
  return new Promise(function(resolve, reject) {
    resolve(p + " test3");
  });
}

test1()
  .then(test2)
  .then(test3)
  .then(console.log)
Mark
  • 90,562
  • 7
  • 108
  • 148
  • That is nice and cleaner for sure, but there is a problem in here: In console log I see: Test1 Finished Test2 Test3 while I have to see Test1, Test2, Test3 and Finish. But the resolved really pass between the promises functions. –  Nov 09 '17 at 06:25
1

First of all, the code blocks that you proposed are not equivalent.

The "manual" way that you show says, "run test1, then test2 with the value returned from test1, then run test3 with the value returned from test2.

The Promise.all way will fire all three functions, effectively, simultaneously and will not feed the return value from one to the next (promise.waterfall).


You may want to try async functions. They provide a more terse, synchronous looking script.

async function test1() {
    return "test1";
}

async function test2(p) {
    return `test2[${p}]`;
}

async function test3(p) {
    return `test3[${p}]`;
}

(async function () {
    let t1 = await test1();
    console.log(`Look what I got: ${t1}`);
    let t2 = await test2(t1);
    console.log(`And here: ${t2}`);
    let t3 = await test3(t2);
    console.log(`Also here: ${t3}`);

    return {
        t1,
        t2,
        t3
    };
}()).then(console.log);

Running this gives you the output:

Look what I got: test1
And here: test2[test1]
Also here: test3[test2[test1]]
{ t1: 'test1', t2: 'test2[test1]', t3: 'test3[test2[test1]]' }

In response to guest271314's comment:

Why would a "Promise chain" need to be broken?

I see the need to break the chain if you need to use an earlier resolution in a later sequence, but don't want to pass a conglomeration of all the results in the chain down. You can either nest callbacks which kind of defeats the point of Promises, or you could use async functions.

Consider:

const test1 = () => Promise.resolve("test1"),
    test2 = (a) => Promise.resolve(`[${a}]test2`),
    test3 = (a) => Promise.resolve(`[${a}]test3`),
    test4 = (a) => Promise.resolve(`[${a}]test4`),
    test5 = (a) => Promise.resolve(`[${a}]test5`),
    greedy = (a, b) => Promise.resolve(`I need ${a} and ${b}`);

// Pass through
test1()
    .then(test2)
    .then(test3)
    .then(test4)
    .then(test5)
    // Greedy should actually be getting the result of test1 and test5
    .then(greedy)
    .then(console.log);

// "Breaking" the chain
test1()
    .then((a) => {
        return test2(a)
            .then(test3)
            .then(test4)
            .then(test5)
            .then((b) => {
                /*
                 * I have to nest if I want to give greedy
                 * the results of test1 and test5
                 */
                return greedy(a, b);
            });
    })
    .then(console.log);

// Using async
(async function () {
    const t1 = await test1(),
        t2 = await test2(t1),
        t3 = await test3(t2),
        t4 = await test4(t3),
        t5 = await test5(t4),
        // I already have t1 and t5 populated
        g = await greedy(t1, t5);
    return g;
}()).then(console.log);

Prints:

I need [[[[test1]test2]test3]test4]test5 and undefined
I need test1 and [[[[test1]test2]test3]test4]test5
I need test1 and [[[[test1]test2]test3]test4]test5
zero298
  • 25,467
  • 10
  • 75
  • 100
  • What is the issue with code at Question? – guest271314 Nov 08 '17 at 21:48
  • @guest271314 I am answering under the presumption that the OP is trying to find a way to retain references to the results of the `test*` functions. I am assuming that is why the `Promise.all()` is appealing since you get a nice array of the results of all the `Promise`s you give it. My answer shows a way to have your cake and eat it too, especially since, as I pointed out, the code blocks initially proposed are not equivalent. – zero298 Nov 08 '17 at 21:50
  • The code at Question can pass the result of previous `Promise` value to next function which returns a `Promise`. OP simply does not return the value from the previous function which returns a `Promise` value to the next function which return a `Promise` – guest271314 Nov 08 '17 at 21:59
  • @guest271314 Then maybe I am misunderstanding the question. I read "*after first promise is done, I can continue using it's resolved data in the next promise*" as, "I want to keep the concrete value around, but I also want to feed it to the next function". To me, that necessitates breaking the Promise chain. If it is as you are interpreting it, then yes, you can just feed continuously as per Mark_M's answer. – zero298 Nov 08 '17 at 22:02
  • Why would a "Promise chain" need to be broken? – guest271314 Nov 08 '17 at 22:03
  • @guest271314 Does my edit make sense? – zero298 Nov 08 '17 at 22:31
  • OPs code is simply lacking passing the previous `Promise` value to next function which returns a `Promise` – guest271314 Nov 08 '17 at 22:33
  • Nothing needs to be broken. I just want to kick out the "manually" way with then return then return then return that is very unreadable and syntax errors vulnerable! Promise.all does good work but not preserves the resolved data in this flow. Think about adding another cluster / function / promise in later situation, when using the "the manual way" you will forget add it to the handle promise for sure. No error will be displayed, just a user in db with missing details. –  Nov 09 '17 at 07:06
0

I would recommend checking out the async functions zero298 recommends. For example (code not tested):

async function test1() {
    return new Promise(function(resolve, reject) {
      resolve("test1");
    });
}

async function test2(p) {
    return new Promise(function(resolve, reject) {
      resolve("test2");
    });
}

async function test3(p) {
    return new Promise(function(resolve, reject) {
      resolve("test3");
    });
}

var test1 = await test1();
var test2 = await test2(test1);
var test3 = await test3();

console.log("Finished");
Tim Hutchison
  • 3,483
  • 9
  • 40
  • 76