I am working on a javascript project that needs to get some data and process it, but I am having trouble with the asynchronous nature of JavaScript. What I want to be able to do is something like the following.
//The set of functions that I want to call in order
function getData() {
//gets the data
}
function parseData() {
//does some stuff with the data
}
function validate() {
//validates the data
}
//The function that orchestrates these calls
function runner() {
getData();
parseData();
validate();
}
Here I want each function to wait for completion before going on to the next call, as I am running into the situation where the program attempts to validate the data before it has been retrieved. However, I also want to be able to return a value from these functions for testing, so I can't have these functions return a boolean value to check completion. How can I make javascript wait on the function to run to completion before moving on to the next call?
Use promises:
//The set of functions that I want to call in order
function getData(initialData) {
//gets the data
return new Promise(function (resolve, reject) {
resolve('Hello World!')
})
}
function parseData(dataFromGetDataFunction) {
//does some stuff with the data
return new Promise(function (resolve, reject) {
resolve('Hello World!')
})
}
function validate(dataFromParseDataFunction) {
//validates the data
return new Promise(function (resolve, reject) {
resolve('Hello World!')
})
}
//The function that orchestrates these calls
function runner(initialData) {
return getData(initialData)
.then(parseData)
.then(validate)
}
runner('Hello World!').then(function (dataFromValidateFunction) {
console.log(dataFromValidateFunction);
})
Not only are they easy to grasp, it makes total sense from a code readability stand point. Read more about them here. If you are in a browser environment, I recommend this polyfill.
The code you've quoted will run synchronously. JavaScript function calls are synchronous.
So I'm going to assume that getData, parseData, and/or validate involve asynchronous operations (such as using ajax in a browser, or readFile in NodeJS). If so, you basically have two options, both of which involve callbacks.
The first is to just have those functions accept callbacks they'll call when done, for instance:
function getData(callback) {
someAsyncOperation(function() {
// Async is done now, call the callback with the data
callback(/*...some data...*/);
});
}
you'd use that like this:
getData(function(data) {
// Got the data, do the next thing
});
The problem with callbacks is that they're hard to compose and have fairly brittle semantics. So promises were invented to give them better semantics. In ES2015 (aka "ES6") or with a decent promises library, that would look something like this:
function getData(callback) {
return someAsyncOperation();
}
or if someAsyncOperation is not promise-enabled, then:
function getData(callback) {
return new Promise(function(resolve, reject) {
someAsyncOperation(function() {
// Async is done now, call the callback with the data
resolve(/*...some data...*/);
// Or if it failed, call `reject` instead
});
});
}
Doesn't seem to do much for you, but one of the key things is composability; your final function ends up looking like this:
function runner() {
return getData()
.then(parseData) // Yes, there really aren't () on parseData...
.then(validate); // ...or validate
}
usage:
runner()
.then(function(result) {
// It worked, use the result
})
.catch(function(error) {
// It failed
});
Here's an example; it will only work on a fairly recent browser that supports Promise and ES2015 arrow functions, because I was lazy and wrote it with arrow functions and didn't include a Promise lib:
"use strict";
function getData() {
// Return a promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// Let's fail a third of the time
if (Math.random() < 0.33) {
reject("getData failed");
} else {
resolve('{"msg":"This is the message"}');
}
}, Math.random() * 100);
});
}
function parseData(data) {
// Note that this function is synchronous
return JSON.parse(data);
}
function validate(data) {
// Let's assume validation is synchronous too
// Let's also assume it fails half the time
if (!data || !data.msg || Math.random() < 0.5) {
throw new Error("validation failed");
}
// It's fine
return data;
}
function runner() {
return getData()
.then(parseData)
.then(validate);
}
document.getElementById("the-button").addEventListener(
"click",
function() {
runner()
.then(data => {
console.log("All good! msg: " + data.msg);
})
.catch(error => {
console.error("Failed: ", error && error.message || error);
});
},
false
);
<input type="button" id="the-button" value="Click to test">
(you can test more than once)
You should change each function to return a Promise, which will allow your final function to become:
function runner() {
return Promise.try(getData).then(parseData).then(validate);
}
To do that, the body of each function should be wrapped in a new promise, like:
function getData() {
return new Promise(function (res, rej) {
var req = new AjaxRequest(...); // make the request
req.onSuccess = function (data) {
res(data);
};
});
}
This is a very cursory example of how promises might work. For more reading, check out:
2ality's fantastic blog posts: part 1 and part 2
bluebird's documentation on why promises
mdn's documentation on JS' Promise class
Related
in regards to Promises in node, what are the main differences between using let (or var and const) to define a function as opposed to creating a function on its own?
let balanceFunc = new Promise(function (resolve, reject) {
exchange.balance((error, balances) => {
if (error) reject(error); else resolve(balances);
});
});
async function test() {
await balanceFunc.then((result) => {
console.log(result.MSFT);
}).catch((error) => {
console.log(error);
});
}
test()
vs
function balanceFunc() {
return new Promise(function (resolve, reject) {
exchange.balance((error, balances) => {
if (error) reject(error); else resolve(balances);
});
});
}
async function test() {
await balanceFunc().then((result) => {
console.log(result.MSFT);
}).catch((error) => {
console.log(error);
});
}
test()
Is the only difference between cosmetics or are there certain cases in which one would be preferable. What about if there were multiple arguments in the function?
The difference is that in the first case:
let balanceFunc = new Promise(function (resolve, reject) {
this creates the Promise and calls exchange.balance as soon as the line runs. In contrast, when you have a function wrapper around it in your second code, the Promise creation and the API call will only occur when the function is called.
For example, if this is intended for consumers of this module, and you did:
export const balanceFunc = new Promise(function (resolve, reject) {
// ...
This would result in exchange.balance being called only once, when the module is initially loaded. For APIs that return different values depending on the time at which they're called, this is almost certainly not what you want: in contrast, using the function version of balanceFunc will call the API anew each time the function is called.
If the API you're using needs to be requested only once, creating a single Promise instead of wrapping a function around it is just fine. (You could still wrap it in a function, of course, but you'd probably want to make sure to only call that wrapper function once)
I'd like to accomplish the following using promises: only execute further once the state of something is ready. I.e. like polling for an external state-change.
I've tried using promises and async-await but am not getting the desired outcome. What am I doing wrong here, and how do I fix it?
The MDN docs have something similar but their settimeout is called within the promise--that's not exactly what I'm looking for though.
I expect the console.log to show "This function is now good to go!" after 5 seconds, but instead execution seems to stop after calling await promiseForState();
var state = false;
function stateReady (){
state = true;
}
function promiseForState(){
var msg = "good to go!";
var promise = new Promise(function (resolve,reject){
if (state){
resolve(msg);
}
});
return promise;
}
async function waiting (intro){
var result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
What you're doing wrong is the promise constructor executor function executes immediately when the promise is created, and then never again. At that point, state is false, so nothing happens.
Promises (and async/await) are not a replacement for polling. You still need to poll somewhere.
The good news: async functions make it easy to do conditional code with loops and promises.
But don't put code inside promise constructor executor functions, because of their poor error handling characteristics. They are meant to wrap legacy code.
Instead, try this:
var state = false;
function stateReady() {
state = true;
}
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
async function promiseForState() {
while (!state) {
await wait(1000);
}
return "good to go!";
}
async function waiting(intro) {
var result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
Based on your comments that you are waiting for messages from a server it appears you are trying to solve an X/Y problem. I am therefore going to answer the question of "how do I wait for server messages" instead of waiting for global variable to change.
If your network API accepts a callback
Plenty of networking API such as XMLHttpRequest and node's Http.request() are callback based. If the API you are using is callback or event based then you can do something like this:
function myFunctionToFetchFromServer () {
// example is jQuery's ajax but it can easily be replaced with other API
return new Promise(function (resolve, reject) {
$.ajax('http://some.server/somewhere', {
success: resolve,
error: reject
});
});
}
async function waiting (intro){
var result = await myFunctionToFetchFromServer();
console.log(intro + result);
}
If your network API is promise based
If on the other hand you are using a more modern promise based networking API such as fetch() you can simply await the promise:
function myFunctionToFetchFromServer () {
return fetch('http://some.server/somewhere');
}
async function waiting (intro){
var result = await myFunctionToFetchFromServer();
console.log(intro + result);
}
Decoupling network access from your event handler
Note that the following are only my opinion but it is also the normal standard practice in the javascript community:
In either case above, once you have a promise it is possible to decouple your network API form your waiting() event handler. You just need to save the promise somewhere else. Evert's answer shows one way you can do this.
However, in my not-so-humble opinion, you should not do this. In projects of significant size this leads to difficulty in tracing the source of where the state change comes form. This is what we did in the 90s and early 2000s with javascript. We had a lot of events in our code like onChange and onReady or onData instead of callbacks passed as function parameters. The result was that sometimes it takes you a long time to figure out what code is triggering what event.
Callback parameters and promises forces the event generator to be in the same place in the code as the event consumer:
let this_variable_consumes_result_of_a_promise = await generate_a_promise();
this_function_generate_async_event((consume_async_result) => { /* ... */ });
From the wording of your question you seem to be wanting to do this instead;
..somewhere in your code:
this_function_generate_async_event(() => { set_global_state() });
..somewhere else in your code:
let this_variable_consumes_result_of_a_promise = await global_state();
I would consider this an anti-pattern.
Calling asynchronous functions in class constructors
This is not only an anti-pattern but an impossibility (as you've no doubt discovered when you find that you cannot return the asynchronous result).
There are however design patterns that can work around this. The following is an example of exposing a database connection that is created asynchronously:
class MyClass {
constructor () {
// constructor logic
}
db () {
if (this.connection) {
return Promise.resolve(this.connection);
}
else {
return new Promise (function (resolve, reject) {
createDbConnection(function (error, conn) {
if (error) {
reject(error);
}
else {
this.connection = conn; // cache the connection
resolve(this.connection);
}
});
});
}
}
}
Usage:
const myObj = new MyClass();
async function waiting (intro){
const db = await myObj.db();
db.doSomething(); // you can now use the database connection.
}
You can read more about asynchronous constructors from my answer to this other question: Async/Await Class Constructor
The way I would solve this, is as follows. I am not 100% certain this solves your problem, but the assumption here is that you have control over stateReady().
let state = false;
let stateResolver;
const statePromise = new Promise( (res, rej) => {
stateResolver = res;
});
function stateReady(){
state = true;
stateResolver();
}
async function promiseForState(){
await stateResolver();
const msg = "good to go!";
return msg;
}
async function waiting (intro){
const result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
Some key points:
The way this is written currently is that the 'state' can only transition to true once. If you want to allow this to be fired many times, some of those const will need to be let and the promise needs to be re-created.
I created the promise once, globally and always return the same one because it's really just one event that every caller subscribes to.
I needed a stateResolver variable to lift the res argument out of the promise constructor into the global scope.
Here is an alternative using .requestAnimationFrame().
It provides a clean interface that is simple to understand.
var serverStuffComplete = false
// mock the server delay of 5 seconds
setTimeout(()=>serverStuffComplete = true, 5000);
// continue until serverStuffComplete is true
function waitForServer(now) {
if (serverStuffComplete) {
doSomethingElse();
} else {
// place this request on the next tick
requestAnimationFrame(waitForServer);
}
}
console.log("Waiting for server...");
// starts the process off
requestAnimationFrame(waitForServer);
//resolve the promise or whatever
function doSomethingElse() {
console.log('Done baby!');
}
I am new to Promises and I'm having a little trouble with the concept of waiting for a nested promise to finish all executions before resolving the original promise.
Original Code
function getSomething(text) {
return new Promise(function (resolve, reject) {
getElse(text).then(function (items) {
if (items.length !== 0) {
/* Stuff here */
getElseElse(box).then(function (moreItems) {
/* Stuff here */
return array;
}.then(function (array) {
var promise = new Promise(function (resolve, reject) {
/* anotherFunction() is asynchronous */
result = anotherFunction(array); <-- Spot 1
});
promise.then(function () { });
});
return resolve(result); <--- Spot 2
}
else {
return resolve(null);
}
});
});
};
Updated Code - better but still not fully working like I want.
function getSomething(text) {
return getElse(text).then(function (items) {
if (items.length !== 0) {
/* Stuff here */
return getElseElse(box).then(function (moreItems) {
/* Stuff here */
return array;
}).then(anotherFunction);
} else {
return null;
}
});
}
Then, inside of the individual View page, I have this:
getSomething(text).then(function (result) {
/* Do something here using result */
/* But result is undefined and I think still pending */
});
I have optimized the original function using Thomas's help but the original call inside the view still seems to be continuing before result is formulated.
I want getSomething() to full complete and return result before I execute the code inside of the .then that is inside the view. So far, that is not being accomplished.
I have found a couple posts that have pointed me in what I think is the correct direction of Promise.all but I can't really seem to get anywhere with that information so I was hoping someone could help explain it for my specific situation.
The posts that have helped are:
Promise Chain not waiting for promises to resolve before ending
Wait until nested promises resolve
Wait for promises inside Promise.all to finish before resolving it
Solution
The original issue was solved by Thomas in the marked answer. The final problem was solved inside of anotherFunction() upon further inspection.
Originally:
function anotherFunction(array) {
return new Promise(function (resolve, reject) {
return anotherGet(parcels).then(function (list) {
/* Do stuff here without a return */
});
});
}
Fixed Version:
function anotherFunction(array) {
return anotherGet(parcels).then(function (list) {
/* Do stuff here */
return list;
});
}
First, avoid the Promise/Deferred antipattern. Rarely you need to create your own promises, usually you have some function that returns a Promise; use that.
Second, for the outer Promise to wait for a nesed PRomise, you need to return that nested Promise to the outer Promise.
Returning a Promise inside of then() will make the resulting Promise to resolve to the value of the Promise you retuned inside.
And last, something like .then(value => value) or .then(value => Promise.resolve(someFunction(value)))is pointless. Your wrapper around anotherFunction basically just passed through the argument and returned the result.
So, your Pseudocode should look something like this:
function getSomething(text) {
return getElse(text).then(function (items) {
if (items.length !== 0) {
/* Stuff here */
return getElseElse(box).then(function (moreItems) {
/* Stuff here */
return array;
}).then(anotherFunction);
} else {
return null;
}
});
}
The basic trick is to make sure all of your resolve() calls are inside of the nested promises' then() methods.
Something along the lines of:
function getSomething(text) {
return new Promise(function (resolve, reject) {
getElse(text).then(function (items) {
if (items.length !== 0) {
/* Stuff here */
getElseElse(box).then(function (moreItems) {
/* Stuff here */
return array;
}.then(function (array) {
var promise = new Promise(function (resolve, reject) {
result = anotherFunction(array); <-- Spot 1
});
promise.then(function () { });
resolve(result); <--- Spot 2
});
}
else {
resolve(null);
}
});
});
};
If it needs to be nested in the promise.then() above, just stick it in there instead.
Also note that you don't need to return your resolves. Just call them.
I am learning Javascript. This may sound like a crazy idea but I don't find clear answers on Google. Can I just replace all my regular functions/methods with async function everywhere? I mean there seems to be no downsides to this and it makes things so much simpler. Where the situation only involves synchronous steps, you just don't use await and it will work just like normal functions. Easy!
Personally I find having to distinguish async functions and normal functions unnecessarily burdensome. It's like driving a manual transmission car, where the car itself could easily handle the gearing by itself.
Am I missing something here?
async functions always return Promises. This means that anytime you're not dealing with something asynchronous, you would have to convert the returned Promise into a value before you could use it. For example:
const return4 = async () => 4;
console.log(4 + return4());
Instead, you would have to use the (unnecessarily wordy):
const return4 = async () => 4;
(async () => {
console.log(4 + await return4());
})();
(or call .then on the return4 call before using it)
If return4 was not async, on the other hand, of course console.log(4 + return4()); alone would work just fine.
Another problem with async functions is that transpiling them to ES5 code (which allows for compatibility with obsolete browsers like IE) requires regenerator-runtime, which is very heavyweight. For example, transpiling the following single line with Babel:
const foo = async () => console.log('async!');
When you plug it into the repl, you get:
"use strict";
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
var foo =
/*#__PURE__*/
(function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
return _context.abrupt("return", console.log("async!"));
case 1:
case "end":
return _context.stop();
}
}
}, _callee);
})
);
return function foo() {
return _ref.apply(this, arguments);
};
})();
which also depends on regeneratorRuntime already being included in the script, which is 700-something lines of code that you can see here.
On less powerful systems, this can result in a not-insignificant performance hit. This is why some (such as with the AirBNB style guide) prefer to never use async functions, even if they make the asynchronous control flow of the script clearer.
In a middle of already completed javascript function, you want to call a new async function. So you need to pass, "rest of the code" as a callback function as parameter to this new function.
function sample() {
alert("a bunch of codes");
alert("another a bunch of codes");
}
I have to change the function as below.
function sample() {
alert("a bunch of codes");
var cb = function () {
alert("another a bunch of codes");
};
newFunction(cb);
}
What if I want to add another function that has to wait first one ? Then I got numerous multiple levels of callback functions to the wait another..
So what is the best practice on ES5 ?
In ES5, just like you said you have to nest multiple callbacks inside each other.
Example:
function myFunction2(){
console.log(2);
let myFunction = () => {
console.log(1);
}
myFunction();
}
myFunction2();
// OUTPUT
// 2
// 1
ES6 also provides a new alternative, promises.
Example:
let myPromise = new Promise((resolve, reject) => {
setTimeout(function(){
resolve(1);
}, 250);
});
console.log(2);
myPromise.then((successMessage) => {
console.log(successMessage);
});
// OUTPUT
// 2
// 1
ES8 has provides an even better alternative(although it is just syntactic sugar based on promises) but you can use async functions with await.
Example:
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function add1(x) {
const a = await resolveAfter2Seconds(20);
const b = await resolveAfter2Seconds(30);
return x + a + b;
}
add1(10).then(v => {
console.log(v); // prints 60 after 4 seconds.
});
Keep in mind though, that you probably need to use Babel to transpile your js in order to be compatible with all browsers.