How to Chain These Functions Together with Promises

How can I chain these functions together with promises?

If you want to chain those functions with promises, then they have to return promises.

If you want to chain them with async module, then they have to take callbacks as arguments.

Right now they neither return a promise (or anything else), nor do they take callbacks (or anything else) as arguments. If the function doesn't take a callback and doesn't return anything then all you can do is call it and that's it. You will not be notified of any result.

Example

Callbacks

If you have 3 functions that take callbacks:

function fun1(cb) {
setTimeout(() => {
cb(null, "fun1");
}, 1000);
}
function fun2(cb) {
setTimeout(() => {
cb(null, "fun2");
}, 3000);
}
function fun3(cb) {
setTimeout(() => {
cb(null, "fun3");
}, 100);
}

Then you can know when they finish:

fun3((err, value) => {
console.log('fun3 finished:', value);
});

And you can easily wait for one before you start the other:

fun1((err1, val1) => {
fun2((err2, val2) => {
console.log("fun1 + fun2:", val1, val2);
});
});

Promises

If your functions return promises:

function fun1() {
return new Promise((res, rej) => {
setTimeout(() => {
res("fun1");
}, 1000);
});
}
function fun2() {
return new Promise((res, rej) => {
setTimeout(() => {
res("fun2");
}, 3000);
});
}
function fun3() {
return new Promise((res, rej) => {
setTimeout(() => {
res("fun3");
}, 100);
});
}

Then you can also know when they finish:

fun3().then(value => {
console.log('fun3 finished:', value);
});

You can also easily nest the calls:

fun1().then(val1 => {
fun2().then(val2 => {
console.log("fun1 + fun2:", val1, val2);
});
});

Or:

fun1()
.then(val1 => fun2())
.then(val2 => fun3())
.then(val3 => console.log('All 3 finished in series'));

etc.

To be able to do much more with both style, see documentation for:

  • http://caolan.github.io/async/
  • http://bluebirdjs.com/docs/getting-started.html
  • https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise

How do I chain a set of functions together using promises and q in node.js?

With these lines

for(var i = 0; i < fruits.length; i++) {
fruitFuncs[i] = makeFruitPie(fruits[i])
}

you already run the functions and, hence, their processing will begin.

Assuming you want the execution of the functions in sequence, the following would be more appropriate:

// construct the pipeline
const start = Q.defer();
let result = start.promise; // we need something to set the pipeline off
fruits.forEach( (fruit) => {
result = result.then( () => makeFruitPie( fruit ) );
});

// start the pipeline
start.resolve();


Sidenote: There is a native Promise implementation supported by almost all environments. Maybe consider switching from the library backed version.

NodeJS: Chain functions automatically in a promise?

Since you said it may be a variable length array and you show sequencing the requests, you can just loop through the array using async/await:

async function do_api_get_requests(API_IDS) {
for (let id of API_IDS) {
const data = await axios.get(`https://my.api.com/${id}`);
// do something with data here
}
return "success";
}

And, since you said the list of API ids would be variable, I made it a parameter that you can pass into the function.


If you wanted to run all the API requests in parallel (which might be OK for a small array, but might be trouble for a large array) and you don't need to run them in a specific order, you can do this:

function do_api_get_requests(API_IDS) {
return Promise.all(API_IDS.map(async (id) => {
const data = await axios.get(`https://my.api.com/${id}`);
// do something with data here for this request
})).then(() => {
// make resolved value be "success"
return "success";
});
}

Depending upon your circumstances, you could also use Promise.allSettled(). Since you don't show getting results back, it's not clear whether that would be useful or not.

How to chain promises together in JavaScript

For the purposes of explaining why they all finish at the same time, we can ignore everything except the promise declarations:

const promise1 = new Promise((resolve, reject) => { setTimeout(resolve, 5000, 'a'); });
const promise2 = new Promise((resolve, reject) => { setTimeout(resolve, 5000, 'b'); });
const promise3 = new Promise((resolve, reject) => { setTimeout(resolve, 5000, 'c'); });

These promises are created HOT (i.e. the handler passed to the constructor is immediately invoked), and were all created at the same time. It follows that the setTimeout within will fire in 5 seconds time, no matter how they are subsequently used.

How they are used subsequently is incidental to this, however, to get your example working, it might be better to write functions that return Promise when called... so:

const getPromise1 = () => new Promise((resolve, reject) => {
setTimeout(resolve, 5000, 'a');
});
const getPromise2 = () => new Promise((resolve, reject) => {
setTimeout(resolve, 5000, 'b');
});
const getPromise3 = () => new Promise((resolve, reject) => {
setTimeout(resolve, 5000, 'c');
});

getPromise1().then((val1) => {
console.log(val1);
return getPromise2(); //by returning Promise, you can avoid deep nesting
}).then((val2) => {
console.log(val2);
return getPromise3();
}).then((val3) => {
console.log(val3);
});

Promise chaining - should all functions need to be promises

The question has become a bit broad because you're still learning a lot of this stuff. :-) Here are the three parts I see that I think I can now reasonably answer:

  1. When should I use Promise.promisify vs. just using then? What do they do differently from one another?

  2. Can I use synchronous functions with then?

  3. How should I write asynchronous functions so that I can use them with promises?

1. When should I use Promise.promisify vs. just using then? What do they do differently from one another?

Promise.promisify is a function provided by Bluebird and is not part of the standard JavaScript Promise object. Its job is to create a wrapper function around a standard Node-callback-style function* that provides a promise instead. It accepts a function and returns a new function wrapped around it.

then is a standard feature of promises that hooks up a callback to the promise's resolution (and optionally a handler to its rejection). It accepts a function and returns a new promise that will resolve or reject depending on what the function you give it does.

They're completely unrelated, other than both involving promises. They do completely different things. The only reason for using Promise.promisify is if you have to deal with legacy Node-style-callback functions (like those in the majority of the Node API, since it predates promises) and want to use promises with them. In contrast, you use then any time you use promises.

2. Can I use synchronous functions with then?

Yes. There's no particular reason to, but you can. The function you pass then gets called with the resolution value of the promise (or its rejection value if you pass the callback as the second argument) as its only argument. If your then callback returns a promise, then makes the promise it creates resolve or reject based on the promise the function returns; if the then callback returns a non-promise value, the promise then creates is resolved with that value.

3. How should I write asynchronous functions so that I can use them with promises?

Make your function return a promise.

Looking at your func1, for instance, it doesn't return a promise and in fact it doesn't work properly:

// Your `func1` from the question
function func1 (filePath1) {
fs.readFile(filePath1, (err, data1) => {
if (err) throw err;
console.log(data1);
});
}

The reason it doesn't work properly is that it throws from the readFile callback. But throws in that callback are not handled by anything usefully.

To write that function to be used with promises, you'd have it return a promise. You can do that by writing it like this:

// `func1` updated to use promises, and also to accept options
function func1(filePath1, options) {
return new Promise((resolve, reject) => {
fs.readFile(filePath1, options, (err, data1) => {
if (err) {
reject(err);
} else {
resolve(data1);
}
});
});
}

...or, if you're using Bluebird, by simply using Promise.promisify, since all your function does is call readFile:

const func1 = Promise.promisify(fs.readFile);

If you're not using Bluebird, or if you want to promisify entire APIs (like the whole of fs) in one go, you might look at the promisify package instead.


* A standard "Node-callback-style function" is one that

  1. Accepts arguments where the last in the list is a callback function
  2. oes its work asynchronously, then
  3. Calls the callback with an initial argument which is either an error or null, followed (if it's null) by the result of the asynchronous call.

Let's look at an example: fs.readFile: It accepts the path of the file to read, an optional options object, and as the last argument a callback to call with the results. When it calls the callback, if there was an error, it passes that error as the first argument and no second argument at all. If there wasn't an error, it calls the callback with two arguments: null, and the file data.

So there's a pattern there:

  • The last argument to the API function is the callback
  • The first argument to the callback is an error or null

Promise.promisify puts a promise-enabled wrapper around any function that works in that way.

So: When should you use it? Any time you want to use promises with a Node-callback-style function.



Related Topics



Leave a reply



Submit