JavaScript typing utilities, like Flow and TypeScript, have become popular in JavaScript apps of all sizes. As I mentioned in our Script & Style Show typing podcast, typing is a great way to implicitly implement documentation and validation. Flow isn't always easy to perfect, however, and
When using Flow,
Cannot call Object.values(…).map with function bound to callbackfn because property {prop} is missing in mixed [1] in the first argument.
Object.values was a pain point for me.When using Flow,
Object.values could trigger the following error:Cannot call Object.values(…).map with function bound to callbackfn because property {prop} is missing in mixed [1] in the first argument.
The reason for this error is that
Object.values() could return any value type. One way to get past this annoyance is to use the following:...(Object.values(value1): any)
Using an
any type is never ideal but providing a type with Object.values will help satisfy Flow. In the end, it does make sense that Object.values isn't trusted, because anything could be returned, but having to use any is a tough pill to swallow for type lovers!
JavaScript async/await has changed the landscape of the way we code. We're no longer stuck in callback or
then hell, and our code can feel more "top down" again.
Async functions require the following syntax:
async function funcExample() {}
To use
await with a function, the function needs to be declared with async. That got me to thinking: is it possible to detect if a function is asynchronous?
To detect if a function is asynchronous, use the function's
constructor.name property:const isAsync = funcExample.constructor.name === "Asynchronus";
If the value is Asynchronus, you know the function is
async!
Async functions are my preferred method of working with promises. Knowing if a function is async could be useful as a library creator or a typing/validation utility.
Basic Promise Usage
promise represents the result of an operation (usually an asynchronous operation), it is a wrapper around a value, that may, or may not be available sometimes in the future; We stop to care about this. It’s useful because it permits developers to more easily work with a future value, as it was already available.
The
new Promise() constructor should only be used for legacy async tasks, like usage of setTimeout or XMLHttpRequest. A new Promise is created with the new keyword and the promise provides resolve and reject functions to the provided callback.
Sometimes you don't need to complete an async tasks within the promise -- if it's possiblethat an async action will be taken, however, returning a promise will be best so that you can always count on a promise coming out of a given function. In that case you can simply call
Promise.resolve() or Promise.reject() without using the new keyword.
Since a promise is always returned, you can always use the
then and catch methods on its return value!then
All promise instances get a
then method which allows you to react to the promise. The first then method callback receives the result given to it by the resolve() call:new Promise(function(resolve, reject) {// A mock async action using setTimeoutsetTimeout(function() { resolve(10); }, 3000);}).then(function(result) {console.log(result);});
The
then callback is triggered when the promise is resolved. You can also chain thenmethod callbacks:new Promise(function(resolve, reject) {setTimeout(function() { resolve(10); }, 3000);}).then(function(num) { console.log('first then: ', num); return num * 2; }).then(function(num) { console.log('second then: ', num); return num * 2; }).then(function(num) { console.log('last then: ', num);});
Each
then receives the result of the previous then's return value.
If trigger is called again even though promise has already been resolved, the callback will be triggered immediately. If you make a call after an appointment has been dismissed and rejected, the callback will not be called.
catch
When the promise is rejected catch callback is executed.:
new Promise(function(resolve, reject) {// A mock async action using setTimeoutsetTimeout(function() { reject('Done!'); }, 3000);}).then(function(e) { console.log('done', e); }).catch(function(e) { console.log('catch: ', e); });
We can use anything for the reject method. A frequent pattern is sending an
Error to the catch:reject(Error('Data could not be found'));
finally
The newly introduced finally callback is called regardless of success or failure.
(new Promise((resolve, reject) => { reject("Nope"); })).then(() => { console.log("success") }).catch(() => { console.log("fail") }).finally(res => { console.log("finally") });
Promise.all
Think about JavaScript loaders: there are times when you trigger multiple async interactions but only want to respond when all of them are completed -- that's where
Promise.all comes in. The Promise.all method takes an array of promises and fires one callback once they are all resolved:Promise.all([promise1, promise2]).then(function(results) {// Both promises resolved}).catch(function(error) {// One or more promises was rejected});
A perfect way of thinking about
Promise.all is firing off multiple AJAX (via fetch) requests at one time:var request1 = fetch('/users.json');var request2 = fetch('/articles.json');Promise.all([request1, request2]).then(function(results) {// Both promises done!});
Comments
Post a Comment