Reputation: 482
I have two functions:
To solve this problem I used callbacks like so:
const fs = require("fs");
const sftp = require("./_uploader/sftp");
const pm2 = require("./_uploader/pm2");
const credentials = {
host: "",
port: 123,
username: "",
key: "key",
};
sftp(credentials, () => {
pm2(credentials, () => {
console.log("done");
});
});
sftp
module.exports = ({ host, port, username, key }, cb) => {
...
cb('done')
}
pm2
module.exports = ({ host, port, username, key }, cb) => {
...
cb('done')
}
I know it can be done with the help of Promises or async functions, but all my attempts were unsuccessful. How should it be done properly?
Upvotes: 0
Views: 836
Reputation: 4010
Let's forget about async
functions for a moment. They are just a bit of syntactic sugar on top of Promises. If you don't already have Promises, then they won't do you any good.
Think of Promises as wrapper objects that handle callbacks for you. They really aren't much more than that. When we construct a new Promise, we get passed special resolve
and reject
functions. We can then use one or both of these functions in place of traditional callbacks. So for example, if we wanted to promisify setTimeout:
const timeoutAsPromised = (delay) => {
return new Promise((resolve) => {
setTimeout(resolve, delay);
});
};
We return a Promises immediately. That's important. Our function has to give the Promise back now so it can be used right away. But in place of a callback, we use the resolve
function that the Promise constructor gave us. Now we can call it like this:
timeoutAsPromised(1000)
.then(() => console.log('One second has passed!'));
For your use case, we can do much the same thing. Just take your functions and wrap them in a promisified version:
const sftpAsPromised = (credentials) => {
return new Promise((resolve) => {
sftp(credentials, resolve);
});
};
Though depending on who wrote sftp
and how, it might be just as easy to rewrite it from the ground up to return a Promise instead of taking a callback:
module.exports = ({ host, port, username, key }) => {
return new Promise((resolve) => {
...
resolve('done')
});
};
And heck, if you have a lot of these asynchronous functions, and they all have the same function signature (they take one argument and then a callback), you might even write a little promisify utility to handle them all:
const promisify = (fn) => (arg) => {
return new Promise((resolve) => {
fn(arg, resolve);
});
};
const pm2AsPromised = promisify(pm2);
Okay! Now let's talk about async/await briefly. These are a lovely bit of syntax, but it is important to always remember that they only work on Promises. If you have some asynchronous functions built on callbacks, async/await is useless to you.
Thankfully we just did that work promisifying our callback functions. So lets make a wrapping async
function, and then await
our promisified function calls. You can think of await
as basically just replacing the .then
method.
const handlerServer = async () => {
await sftpAsPromised(credentials);
await pm2AsPromised(credentials);
};
handleServer();
Hopefully that clears things up!
Upvotes: 0
Reputation: 1928
There are different ways to do this. I won't cover them all.
OPTION 1: AWAIT PROMISE
For this example you will want to run the code inside an async
function to take advantage of the await
keyword.
Update your SMTP and PM2 functions to return a Promise. Inside the promise will handle the logic and resolve("done")
releases the promise so that code can move on.
module.exports = ({ host, port, username, key }) => {
return new Promise((resolve) => {
...
resolve("done");
});
}
Now you can update the execution code to take advantage of the promises:
const fs = require("fs");
const sftp = require("./_uploader/sftp");
const pm2 = require("./_uploader/pm2");
const credentials = {
host: "",
port: 123,
username: "",
key: "key",
};
const runApp = async () => {
await sftp(credentials);
await pm2(credentials);
console.log("done");
}
runApp();
OPTION 2: CHAIN PROMISES
Another way to do this is by chaining the Promises. I opt not to do this very often because it can become a mess of nested logic.
const fs = require("fs");
const sftp = require("./_uploader/sftp");
const pm2 = require("./_uploader/pm2");
const credentials = {
host: "",
port: 123,
username: "",
key: "key",
};
sftp(credentials).then(result1 => {
pm2(credentials).then(result2 => {
console.log("done");
});
});
OPTION 3: PROMISE ALL
Another option is to use Promise.all
const fs = require("fs");
const sftp = require("./_uploader/sftp");
const pm2 = require("./_uploader/pm2");
const credentials = {
host: "",
port: 123,
username: "",
key: "key",
};
Promise.all([
sftp(credentials),
pm2(credentials)
]).then(result => {
console.log("done");
});
Upvotes: 1