How can I use a variable outside of the .then() scope
I have the following situation:
Why does the clients variable resolve in undefined when I call it below;
function getClientList() {
TeamSpeak.connect({
host: "localhost",
serverport: 9987,
nickname: "NodeJS Query Framework",
}).then(async (teamspeak) => {
const clients = await teamspeak.clientList({ clientType: 0 });
if (clients) return clients;
else return null;
});
}
When I call it like that it resolves as undefined
const clients = getClientList();
The short answer to your question is that you cannot use a resulting variable until the Promise resolves. Whether you use .then
or await
it's really the same mechanism and you can't access the result of your call until it's ready.
You can only get the results of it by waiting for the Promise
to resolve. This can be done by using await
if your function is marked as async
, or by using the .then()
function on the Promise
since it will get called after the result is ready.
Think of the Promise
as a black box that has a then
function in it. Remember that each .then()
function also returns as a Promise
object which is why you can chain them as promise.then(...).then(...).then(...);
. Your code then looks like this:
function getClientList() {
PROMISE1.then(someFunction);
}
const clients = getClientList();
Note that even though it spans multiple lines, your getClientList
function is really just one line of code TeamSpeak.connect(...).then(...);
There are a few things wrong with your code. Let's go through them one by one:
- The
getClientList
function doesn't return anything explicitly. In its most basic form it'sfunction getClientList() { someCall(); }
and that returnsundefined
by default. That's whyclients
is alwaysundefined
. - If you add a return statement to the function, it will return a
Promise
and might look like this:
function getClientList() {
return PROMISE1.then(someFunction);
}
const clients = getClientList();
This is better because clients
is not undefined
any more, but it will be the Promise
that may not have finished its work yet. To see the result of the call you'd need to use the then
function like clients.then(result => console.log(result))
to allow it to wait for the asynchronous work to complete.
- You're using both
.then
andasync
/await
. It will probably be cleaner and easier to work with if you go with one or the other (await
is my preference). Your code can look like this:
async function getClientList() {
const teamspeak = await TeamSpeak.connect({
host: "localhost",
serverport: 9987,
nickname: "NodeJS Query Framework",
});
const clients = await teamspeak.clientList({ clientType: 0 });
return clients || null; // returns as a Promise resolving to clients or null
}
const clients = await getClientList(); // assuming you're in another async fn
- There is no error handling. If any of the asynchronous calls fail then the
Promise
will either reject or cause an exception. You should handle that by either surrounding it with atry
/catch
block at some level, or by adding.catch()
after the finalthen
, or by adding a second function to the.then()
call to handle a rejection.
So putting it all together your code could look like this:
async function getClientList() {
const teamspeak = await TeamSpeak.connect({
host: "localhost",
serverport: 9987,
nickname: "NodeJS Query Framework",
});
return await teamspeak.clientList({ clientType: 0 }) || null;
}
// Example calling getClientList using ".then"
getClientList()
.then(
client => {
/* use the client value in this scope */
},
reason => {/* report the reason for rejection */}
) // end of .then
.catch(err => {/* report the error */});
// Example calling getClientList using "await"
try {
const client = await getClientList();
/* use the client value in this scope */
} catch (err) {/* report the error */}
No matter how you make the call, you're only able to access the clients
value (returned by calling the clientList
function) within the scope of either the .then
or after await
.