I have a validation library that is purely synchronous but is often used as part of a chain of async functions. However, I have to maintain an existing synchronous API and would like to make the promise API optional.
Can I somehow detect (at runtime) whether a function is part of a Promise chain?
This is pretty easy with callbacks since you can just check if a callback was passed in. I understand that I could pass in an optional promise boolean, but that seems inelegant.
I've also considered doing a callback interface and using a library to convert the callback interface to a promise based interface on the fly. However, I'm working in Haxe and I would prefer to keep the transforms/abstractions down to a minimum.
I also understand that you can sandwich regular functions in-between promises, but there are some instances where the behavior would differ between the two.
FINAL Edit Lots of confusion as to why I can't just return the same values, first example (below) didn't seem to help. Remember, this is still simplified.
//mix of sync with promise
new Promise(function(resolve, reject){
var safeToAdd = thingTracker.preflight(newThing);
if(safeToAdd){
return client.request.addThing(newThing); //send request to server
} else {
reject(newThing.errorMessages); //requires explicit reject, cannot just pass results along
}
}).then(function(newThing){ //client and server both cool with newThing?
thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
ui.show(errorMessages);
});
//pure promise
thingTracker.preflight(newThing).then(function(){
return client.request.addThing(newThing); //sends request to server
}).then(function(newThing){ //client and server both cool with newThing?
thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
ui.show(errorMessages);
});
(Old) Edit to clarify (but didn't really):
function preflight(thing){
var validity = thing === 42;
if(promise){
if(validity){
return Promise.resolve(validity);
} else {
return Promise.reject(validity);
}
} else {
return validity;
}
}
Obviously I can do the same checks in a then's anon function, but that is not much better than using the sync interface directly. Also note that this is a very simple example, in the real function there are side effects on thing and messages are produced.
Edit Just to illustrate my point a bit better, here is a gist of what things look like.
Synchronous functions can be used within a promise chain just fine. If you have a synchronous API call that is called like this:
var info = myApi("foo");
That can be used just fine within a promise chain:
someAsyncCall().then(myApi).then(someOtherFunction)
You don't need to make myApi async or return a promise to be used this way. The only thing you can't do in a promise chain with a synchronous function is that it can't be the first item in the chain, but then it doesn't need to be. Since it's synchronous, it can just be called before you start the chain if anyone wants it to execute first. Or, worst case, you can do:
Promise.resolve().then(myAPI).then(someOtherFunction);
Can I somehow detect (at runtime) whether a function is part of a
Promise chain?
No, you cannot (unless you explicitly pass it some info that tells it that) and you do not need to in order to use it in a promise chain. You do not need to return a promise to be a .then() handler. You can just return a value and that value will become the value of the promise chain at that point in the chain.
I also understand that you can sandwich regular functions in-between
promises, but there are some instances where the behavior would differ
between the two ... such as returning false vs throwing a message.
It is not clear to me why the behavior would have to be different. If returning false is your normal synchronous behavior, you can do that just fine in a promise chain - the next step in the chain just needs to handle the false value that is passed to it, just like the next line of synchronous code would do. If throwing an exception is your normal behavior, you can do that just fine in a promise chain too (the promise chain will turn the exception into a rejection) which the following code can decide how it wants to handle. One can make a good argument that it would be worse if your synchronous function behaved differently based on whether it was part of a promise chain or not. If you see a good reason for two different behaviors, then you should probably have either two different functions (that can be documented differently) or an option passed in that determines the behavior.
Comment based on the code you added in your edit
You seem to think that when called via a promise chain that you need to return a resolved or rejected promise chain. You do not need to do that. You can just return a normal value and the promise chain will inherit the value you return. There is really no reason to return a resolved or rejected promise unless you want it to be the first function in a promise chain, but in that case, it isn't in a promise chain yet anyway so you could never detect that. And, there's no reason for a synchronous operation to be the first one in a promise chain. Just call the synchronous operation first and then initiation your promise chain with the async operations.
Here's a demo of a synchronous function being used in a promise chain:
function log(str) {
var div = document.createElement("div");
div.innerHTML = str;
document.body.appendChild(div);
}
// test async call
function delay(t, val) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(val);
}, t);
});
}
function square(x) {
return x * x;
}
log("Calculating the square of 9");
delay(500, 9).then(square).then(function(result) {
log("Result = " + result);
});
Or, you can even do this to initiate a promise chain with your synchronous operation as the first operation in the chain:
function log(str) {
var div = document.createElement("div");
div.innerHTML = str;
document.body.appendChild(div);
}
function square(x) {
return x * x;
}
log("Calculating the square of 9");
Promise.resolve(9).then(square).then(function(result) {
log("Result = " + result);
});
I believe promises have only one argument so it's not possible to pass an isPromise boolean through the chain, even if you could, you'd have to consciously add the parameter, so why not just call the async function...
function preflight(thing){
return thing === 42;
}
function preflightAsync(thing){
return new Promise(resolver);
function resolver(resolve,reject){
setTimeout(function foo(){
preflight(thing) ? resolve(true,"this won't make it") : reject();
},1000);
}
}
// Promised
preflightAsync(42).then(doSomethingWithIt).catch(doSomethingWithIt);
preflightAsync(89).then(doSomethingWithIt).catch(doSomethingWithIt);
// Call Directly
doSomethingWithIt(preflight(42));
doSomethingWithIt(preflight(89));
function doSomethingWithIt(result,nada){
result = result ? "BAM!!!" : "WAM!!!";
console.log("doSomethingWithIt",result,nada);//doSomethingWithIt BAM!!! undefined
return result;
}
These examples use the new native Promise function, browser support is limited.
As we can see from previous answers you can't use exactly the same function also for Promises. Adding my workaround here which is unfortunately extending the function prototype ...
The problem is that if you want to use the same function synchronously and in a Promise the use of arguments would differ.
As an example - a function truncating a string:
sync:
truncate('A very long string coming from a sync. operation', 10);
async:
Promise.resolve('A very long string coming from a network request')
.then( truncate(10) )
The async version would make the truncate Function think that 10 is the string and you would need to check typeof in every function or do ducktyping.
What would work:
Promise.resolve('A very long string coming from a network request')
.then( truncate.it(10) )
if you extended the function prototype before:
Function.prototype.it = (function() {
return function() {
var args = arguments;
return function(){
Array.prototype.push.apply(arguments, args);
return this.apply(null, arguments);
}.bind(this);
};
}());
function foo(a, b, c) {
console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);
};
// LOGIC :
foo(1, 2, 3);
// SAME AS
foo.it(2, 3)(1);
// OR
foo.it(3)(1, 2);
// OR pretty useful for .then()
Promise.resolve(1).then(foo.it(2, 3));
fiddle
Related
I would like to involve more logic than just return new Promise(...); from a function. I'm not sure if storing a Promise into a variable is changing the "synchronicity" behind it, as every example I've seen does not do that. I would also like to store a Promise into a variable in general to return at the end, even when I'm not involving conditionals, as it just seems bad for debugging and in general to have all the logic reside in a return statement.
function doSomething(){
var promise_var1;
if (condition){
//Do something async, like an AJAX call to an API
setTimeout(function(){promise_var1 = Promise.resolve('a')}, 3000);
}else{
//Do something synchronous, like setting a default value
promise_var1 = Promise.resolve('b');
}
return promise_var1;
}
doSomething().then(function(value){console.log('c'+value)});
I'm currently getting
doSomething() is undefined
however I've tried various ways so I may be misunderstanding something conceptually.
Edit: "Duplicate" question is asking how to handle conditionals inside a promise. I'm asking how to return promises via variable (to achieve single exit). That may not be possible because JavaScript is weakly typed and Promise variable return vs. Promise return may behave differently, but if there is a solution to do that I would prefer single-exit over multiple returns.
Try writing your promise like this.
function doSomething(condition){
var promise_var1= new Promise(function(resolve, reject) {
if(condition){
//resolve after your async action
setTimeout(resolve('a'), 3000);
}else{
//resolve immediately
resolve('b');
}
});
return promise_var1;
}
To answer your question simply: Yes. You can absolutely store a promise in a variable. This is exactly why we have promises. It allows us to "store" a value that may not exist yet, so we can easily pass it around to other parts of the program.
Ultimately you still need to call a callback to get at the internal value, either with .then or with the overly-sweet sugar of async/await. But until you are ready to define your callback, you can pass that future-value around just like any other value.
However, in your example, I don't think the use of a temporary variable does anything to improve the logic or readability of your code. (And in fact, it doesn't work at all, because your if doesn't add a promise to the returned variable until 3 seconds later, after .then has already been called.) Just make sure that every branch of your conditional returns a promise, and you're good to go.
function doSomething(condition){
if (condition){
//Do something async, like an AJAX call to an API
return new Promise(resolve => {
setTimeout(() => resolve('a'), 3000);
})
}
//Don't actually need an else because if condition returns.
//Do something synchronous, like setting a default value.
return Promise.resolve('b');
}
doSomething(true)
.then(value => console.log('c ' + value));
doSomething(false)
.then(value => console.log('c ' + value));
Why make it complicated?
let doSomething = condition => condition ? somethingAsync() : Promise.resolve('b');
function always returns a Promise you can .then whether it resolves in one turn of the event loop or 3 minutes
Update
here's a version with one return and the branch inside the Promise
let doSomething = condition => {
return new Promise(resolve => {
if (condition) {
resolve(someAsyncThing());
} else {
resolve('b');
}
});
};
I would still argue that it's less readable and simple than the version above, but it's your codebase, YMMV.
The following code contains two kinds of promise chain, seems they'r equivalent, but I don't understand why they are equivalent. I think of it like 2*(3+4) = 2*3+4, which is not correct.
// example1
runAsync1()
.then(function(data1){
return runAsync2()
.then(function(data2){
return runAsync3();
})
})
.then(function(data3){
console.log(data3);
});
// example 2
runAsync1()
.then(function(data1){
return runAsync2();
}).then(function(data2){
return runAsync3();
}).then(function(data3){
console.log(data3);
});
Maybe some braces would help you understand this a little better. (NOTE: this is exactly the same thing as you had except with an extra set of braces on the return)
// example1
runAsync1()
.then(function(data1){
return (
runAsync2()
.then(function(data2){
return runAsync3();
})
)
})
.then(function(data3){
console.log(data3);
});
So what is happening here,
.then is called on runAsynch1()
.then is then called on runAsynch2() which returns runAsynch3() (this return carries on all the way down the chain of returns)
finally .then is called on the returned runAsynch3()
As you can see, this is exactly the same process as what happens in example 2
// example 2
runAsync1()
.then(function(data1){
return runAsync2();
}).then(function(data2){
return runAsync3();
}).then(function(data3){
console.log(data3);
});
It's a little bit implementation-specific, but the magic, regardless of the specifics is in the then.
Then does a couple of things.
The TL;DR is that then subscribes to returned promise chains, but below are examples closer to the guts of a promise.
The first thing is:
"Is the promise still pending? Then schedule these callbacks for later; otherwise, schedule this callback to run on the next tick (setTimeout(handle, 0)). Return a new promise that resolves/rejects to the eventual value."
// basically accurate-ish
then (onSuccess, onError) {
return new Promise((resolveNext, rejectNext) => {
if (myPromiseIsDone) {
// I already have the value; run the next step ASAP
// this is ugly, but hopefully it proves a point
runOnNextTick(onSuccess, onError, promiseResult, resolveNext, rejectNext);
} else {
// I don't have a result yet; come back when it's ready
scheduleWhenDone(onSuccess, onError, resolveNext, rejectNext);
}
});
}
The second is where your answer lies:
"Run the handler. Get the return value from the callback passed to then. Is the return value a Thenable object (has a then method)? Then call then and pass in the resolve/reject functions that control my promise... otherwise, resolve/reject accordingly, based on whether it throws or returns."
// entirely subjective and implementation-specific,
// but the algorithm is almost spec-compliant
handleSuccess (onSuccess, resolveNext, rejectNext, value) {
if (!onSuccess) {
// if they didn't give me a `then(callback)`,
// just pass the value to the next promise
resolveNext(value);
return;
}
try {
// I'm running the `then(callback)` here
const result = onSuccess(value);
if (hasThenMethod(result)) {
// it's promise-like, subscribe
result.then(resolveNext, rejectNext);
} else {
resolveNext(result);
}
} catch (err) {
// something blew up, while trying to succeed, so fail
rejectNext(err);
}
}
Reject looks similar.
So the outer then subscribes to the inner then.
If you ever wanted to get into functional programming, the ES6 Promise is almost a monad.
The difference is that a monad would force you to have a different method, to tell it to chain to the returned promise, rather than doing it automatically.
then is basically the same as map on arrays. You call then, pass it a function that gets the value and returns a new promise with the transformed value.
So a monadic promise might be one that has map and chain, or then and chain, where chain does almost exactly the same thing but says "hey, then, this callback is going to return a promise; subscribe to that one, so that you flatten it out, rather than returning a promise of a promise of a value".
That's almost all it would take to be a monad.
Hope that all helps.
I've been developing JavaScript for a few years and I don't understand the fuss about promises at all.
It seems like all I do is change:
api(function(result){
api2(function(result2){
api3(function(result3){
// do work
});
});
});
Which I could use a library like async for anyway, with something like:
api().then(function(result){
api2().then(function(result2){
api3().then(function(result3){
// do work
});
});
});
Which is more code and less readable. I didn't gain anything here, it's not suddenly magically 'flat' either. Not to mention having to convert things to promises.
So, what's the big fuss about promises here?
Promises are not callbacks. A promise represents the future result of an asynchronous operation. Of course, writing them the way you do, you get little benefit. But if you write them the way they are meant to be used, you can write asynchronous code in a way that resembles synchronous code and is much more easy to follow:
api().then(function(result){
return api2();
}).then(function(result2){
return api3();
}).then(function(result3){
// do work
});
Certainly, not much less code, but much more readable.
But this is not the end. Let's discover the true benefits: What if you wanted to check for any error in any of the steps? It would be hell to do it with callbacks, but with promises, is a piece of cake:
api().then(function(result){
return api2();
}).then(function(result2){
return api3();
}).then(function(result3){
// do work
}).catch(function(error) {
//handle any error that may occur before this point
});
Pretty much the same as a try { ... } catch block.
Even better:
api().then(function(result){
return api2();
}).then(function(result2){
return api3();
}).then(function(result3){
// do work
}).catch(function(error) {
//handle any error that may occur before this point
}).then(function() {
//do something whether there was an error or not
//like hiding an spinner if you were performing an AJAX request.
});
And even better: What if those 3 calls to api, api2, api3 could run simultaneously (e.g. if they were AJAX calls) but you needed to wait for the three? Without promises, you should have to create some sort of counter. With promises, using the ES6 notation, is another piece of cake and pretty neat:
Promise.all([api(), api2(), api3()]).then(function(result) {
//do work. result is an array contains the values of the three fulfilled promises.
}).catch(function(error) {
//handle the error. At least one of the promises rejected.
});
Hope you see Promises in a new light now.
Yes, Promises are asynchronous callbacks. They can't do anything that callbacks can't do, and you face the same problems with asynchrony as with plain callbacks.
However, Promises are more than just callbacks. They are a very mighty abstraction, allow cleaner and better, functional code with less error-prone boilerplate.
So what's the main idea?
Promises are objects representing the result of a single (asynchronous) computation. They resolve to that result only once. There's a few things what this means:
Promises implement an observer pattern:
You don't need to know the callbacks that will use the value before the task completes.
Instead of expecting callbacks as arguments to your functions, you can easily return a Promise object
The promise will store the value, and you can transparently add a callback whenever you want. It will be called when the result is available. "Transparency" implies that when you have a promise and add a callback to it, it doesn't make a difference to your code whether the result has arrived yet - the API and contracts are the same, simplifying caching/memoisation a lot.
You can add multiple callbacks easily
Promises are chainable (monadic, if you want):
If you need to transform the value that a promise represents, you map a transform function over the promise and get back a new promise that represents the transformed result. You cannot synchronously get the value to use it somehow, but you can easily lift the transformation in the promise context. No boilerplate callbacks.
If you want to chain two asynchronous tasks, you can use the .then() method. It will take a callback to be called with the first result, and returns a promise for the result of the promise that the callback returns.
Sounds complicated? Time for a code example.
var p1 = api1(); // returning a promise
var p3 = p1.then(function(api1Result) {
var p2 = api2(); // returning a promise
return p2; // The result of p2 …
}); // … becomes the result of p3
// So it does not make a difference whether you write
api1().then(function(api1Result) {
return api2().then(console.log)
})
// or the flattened version
api1().then(function(api1Result) {
return api2();
}).then(console.log)
Flattening does not come magically, but you can easily do it. For your heavily nested example, the (near) equivalent would be
api1().then(api2).then(api3).then(/* do-work-callback */);
If seeing the code of these methods helps understanding, here's a most basic promise lib in a few lines.
What's the big fuss about promises?
The Promise abstraction allows much better composability of functions. For example, next to then for chaining, the all function creates a promise for the combined result of multiple parallel-waiting promises.
Last but not least Promises come with integrated error handling. The result of the computation might be that either the promise is fulfilled with a value, or it is rejected with a reason. All the composition functions handle this automatically and propagate errors in promise chains, so that you don't need to care about it explicitly everywhere - in contrast to a plain-callback implementation. In the end, you can add a dedicated error callback for all occurred exceptions.
Not to mention having to convert things to promises.
That's quite trivial actually with good promise libraries, see How do I convert an existing callback API to promises?
In addition to the already established answers, with ES6 arrow functions Promises turn from a modestly shining small blue dwarf straight into a red giant. That is about to collapse into a supernova:
api().then(result => api2()).then(result2 => api3()).then(result3 => console.log(result3))
As oligofren pointed out, without arguments between api calls you don't need the anonymous wrapper functions at all:
api().then(api2).then(api3).then(r3 => console.log(r3))
And finally, if you want to reach a supermassive black hole level, Promises can be awaited:
async function callApis() {
let api1Result = await api();
let api2Result = await api2(api1Result);
let api3Result = await api3(api2Result);
return api3Result;
}
In addition to the awesome answers above, 2 more points may be added:
1. Semantic difference:
Promises may be already resolved upon creation. This means they guarantee conditions rather than events. If they are resolved already, the resolved function passed to it is still called.
Conversely, callbacks handle events. So, if the event you are interested in has happened before the callback has been registered, the callback is not called.
2. Inversion of control
Callbacks involve inversion of control. When you register a callback function with any API, the Javascript runtime stores the callback function and calls it from the event loop once it is ready to be run.
Refer The Javascript Event loop for an explanation.
With Promises, control resides with the calling program. The .then() method may be called at any time if we store the promise object.
In addition to the other answers, the ES2015 syntax blends seamlessly with promises, reducing even more boilerplate code:
// Sequentially:
api1()
.then(r1 => api2(r1))
.then(r2 => api3(r2))
.then(r3 => {
// Done
});
// Parallel:
Promise.all([
api1(),
api2(),
api3()
]).then(([r1, r2, r3]) => {
// Done
});
Promises are not callbacks, both are programming idioms that facilitate async programming. Using an async/await-style of programming using coroutines or generators that return promises could be considered a 3rd such idiom. A comparison of these idioms across different programming languages (including Javascript) is here: https://github.com/KjellSchubert/promise-future-task
No, Not at all.
Callbacks are simply Functions In JavaScript which are to be called and then executed after the execution of another function has finished. So how it happens?
Actually, In JavaScript, functions are itself considered as objects and hence as all other objects, even functions can be sent as arguments to other functions. The most common and generic use case one can think of is setTimeout() function in JavaScript.
Promises are nothing but a much more improvised approach of handling and structuring asynchronous code in comparison to doing the same with callbacks.
The Promise receives two Callbacks in constructor function: resolve and reject. These callbacks inside promises provide us with fine-grained control over error handling and success cases. The resolve callback is used when the execution of promise performed successfully and the reject callback is used to handle the error cases.
No promises are just wrapper on callbacks
example
You can use javascript native promises with node js
my cloud 9 code link : https://ide.c9.io/adx2803/native-promises-in-node
/**
* Created by dixit-lab on 20/6/16.
*/
var express = require('express');
var request = require('request'); //Simplified HTTP request client.
var app = express();
function promisify(url) {
return new Promise(function (resolve, reject) {
request.get(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
resolve(body);
}
else {
reject(error);
}
})
});
}
//get all the albums of a user who have posted post 100
app.get('/listAlbums', function (req, res) {
//get the post with post id 100
promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) {
var obj = JSON.parse(result);
return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums')
})
.catch(function (e) {
console.log(e);
})
.then(function (result) {
res.end(result);
}
)
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
//run webservice on browser : http://localhost:8081/listAlbums
JavaScript Promises actually use callback functions to determine what to do after a Promise has been resolved or rejected, therefore both are not fundamentally different. The main idea behind Promises is to take callbacks - especially nested callbacks where you want to perform a sort of actions, but it would be more readable.
Promises overview:
In JS we can wrap asynchronous operations (e.g database calls, AJAX calls) in promises. Usually we want to run some additional logic on the retrieved data. JS promises have handler functions which process the result of the asynchronous operations. The handler functions can even have other asynchronous operations within them which could rely on the value of the previous asynchronous operations.
A promise always has of the 3 following states:
pending: starting state of every promise, neither fulfilled nor rejected.
fulfilled: The operation completed successfully.
rejected: The operation failed.
A pending promise can be resolved/fullfilled or rejected with a value. Then the following handler methods which take callbacks as arguments are called:
Promise.prototype.then() : When the promise is resolved the callback argument of this function will be called.
Promise.prototype.catch() : When the promise is rejected the callback argument of this function will be called.
Although the above methods skill get callback arguments they are far superior than using
only callbacks here is an example that will clarify a lot:
Example
function createProm(resolveVal, rejectVal) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
console.log("Resolved");
resolve(resolveVal);
} else {
console.log("Rejected");
reject(rejectVal);
}
}, 1000);
});
}
createProm(1, 2)
.then((resVal) => {
console.log(resVal);
return resVal + 1;
})
.then((resVal) => {
console.log(resVal);
return resVal + 2;
})
.catch((rejectVal) => {
console.log(rejectVal);
return rejectVal + 1;
})
.then((resVal) => {
console.log(resVal);
})
.finally(() => {
console.log("Promise done");
});
The createProm function creates a promises which is resolved or rejected based on a random Nr after 1 second
If the promise is resolved the first then method is called and the resolved value is passed in as an argument of the callback
If the promise is rejected the first catch method is called and the rejected value is passed in as an argument
The catch and then methods return promises that's why we can chain them. They wrap any returned value in Promise.resolve and any thrown value (using the throw keyword) in Promise.reject. So any value returned is transformed into a promise and on this promise we can again call a handler function.
Promise chains give us more fine tuned control and better overview than nested callbacks. For example the catch method handles all the errors which have occurred before the catch handler.
Promises allows programmers to write simpler and far more readable code than by using callbacks.
In a program, there are steps want to do in series.
function f() {
step_a();
step_b();
step_c();
...
}
There's usually information carried between each step.
function f() {
const a = step_a( );
const b = step_b( a );
const c = step_c( b );
...
}
Some of these steps can take a (relatively) long time, so sometimes you want to do them in parallel with other things. One way to do that is using threads. Another is asynchronous programming. (Both approaches has pros and cons, which won't be discussed here.) Here, we're talking about asynchronous programming.
The simple way to achieve the above when using asynchronous programming would be to provide a callback which is called once a step is complete.
// step_* calls the provided function with the returned value once complete.
function f() {
step_a(
function( a )
step_b(
function( b )
step_c(
...
)
},
)
},
)
}
That's quite hard to read. Promises offer a way to flatten the code.
// step_* returns a promise.
function f() {
step_a()
.then( step_b )
.then( step_c )
...
}
The object returned is called a promise because it represents the future result (i.e. promised result) of the function (which could be a value or an exception).
As much as promises help, it's still a bit complicated to use promises. This is where async and await come in. In a function declared as async, await can be used in lieu of then.
// step_* returns a promise.
async function f()
const a = await step_a( );
const b = await step_b( a );
const c = await step_c( b );
...
}
This is undeniably much much more readable than using callbacks.
I am confused about the current discussion of adding async functions and the keyword await to the next EcmaScript.
I do not understand why it is necessary to have the async keyword before the function keyword.
From my point of view the await keyword to wait for a result of a generator or promise done, a function's return should be enough.
await should simple be usable within normal functions and generator functions with no additional async marker.
And if I need to create a function what should be usable as an result for an await, I simply use a promise.
My reason for asking is this good explanation, where the following example comes from:
async function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// some more logic
}
It also could be done as normal function, if the execution of a function will wait for finishing the hole function until all awaits are fulfilled.
function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// return because createUser() and getFacebookFriends() and maybe inviteFacebookFriends() finished their awaited result.
}
In my opinion the whole function execution is holding until the next tick (await fulfillment) is done. The difference to Generator-Function is that the next() is triggering and changing the object's value and done field. A function instead will simple give back the result when it is done and the trigger is a function internal trigger like a while-loop.
I do not understand why it is necessary to have the async keyword before the function keyword.
For the same reason that we have the * symbol before generator functions: They mark the function as extraordinary. They are quite similar in that regard - they add a visual marker that the body of this function does not run to completion by itself, but can be interleaved arbitrarily with other code.
The * denotes a generator function, which will always return a generator that can be advanced (and stopped) from outside by consuming it similar to an iterator.
The async denotes an asynchronous function, which will always return a promise that depends on other promises and whose execution is concurrent to other asynchronous operations (and might be cancelled from outside).
It's true that the keyword is not strictly necessary and the kind of the function could be determined by whether the respective keywords (yield(*)/await) appear in its body, but that would lead to less maintainable code:
less comprehensible, because you need to scan the whole body to determine the kind
more errorprone, because it's easy to break a function by adding/removing those keywords without getting a syntax error
a normal function, whose execution will wait for finishing the hole body until all awaits are fulfilled
That sounds like you want a blocking function, which is a very bad idea in a concurrent setting.
By marking a function as async, you're telling JS to always return a Promise.
Because it will always return a Promise, it can also await on promises inside of its own block. Imagine it like one giant Promise chain - what happens internally to the function gets effectively gets bolted on to its internal .then() block, and what's returned is the final .then() in the chain.
For example, this function...
async function test() {
return 'hello world';
}
... returns a Promise. So you can execute it like one, .then() and all.
test().then(message => {
// message = 'hello world'
});
So...
async function test() {
const user = await getUser();
const report = await user.getReport();
report.read = true
return report;
}
Is roughly analogous to...
function test() {
return getUser().then(function (user) {
return user.getReport().then(function (report) {
report.read = true;
return report;
});
});
}
In both cases, the callback passed to test().then() will receive report as its first parameter.
Generators (i.e. marking a function * and using the yield keyword) are a different concept altogether. They don't use Promises. They effectively allow you to 'jump' between different portions of your code, yielding a result from inside of a function and then jumping back to that point and resuming for the next yield block.
Although they feel somewhat similar (i.e. 'halting' execution until something happens somewhere else), async/await only gives you that illusion because it messes with the internal ordering of Promise execution. It's not actually waiting - it's just shuffling when the callbacks happen.
Generators, by contrast, are implemented differently so that the generator can maintain state and be iterated over. Again, nothing to do with Promises.
The line is further blurred because at the current time of writing, support for async/await is scare; Chakracore supports it natively, and V8 has it coming soon. In the meantime, transpilers like Babel allow you to write async/await and convert the code to generators. It's a mistake to conclude that generators and async/await are therefore the same; they're not... it just so happens that you can bastardize how yield works alongside Promises to get a similar result.
Update: November 2017
Node LTS now has native async/await support, so you ought never to need to use generators to simulate Promises.
These answers all give valid arguments for why the async keyword is a good thing, but none of them actually mentions the real reason why it had to be added to the spec.
The reason is that this was a valid JS pre-ES7
function await(x) {
return 'awaiting ' + x
}
function foo() {
return(await(42))
}
According to your logic, would foo() return Promise{42} or "awaiting 42"? (returning a Promise would break backward compatibility)
So the answer is: await is a regular identifier and it's only treated as a keyword inside async functions, so they have to be marked in some way.
The reason for the async keyword in front is simple so you know that return value will be transformed into a promise. If no keyword how would the Interpreter know to do this.
I think this was first introduce in C# and EcmaScript is taking a loot of stuff from TypeScript. TypeScript and C# are conceived by Anders Hejlsberg and are similar.
lets say you have a function (this one is just to have some asynchronous work)
function timeoutPromise() {
return (new Promise(function(resolve, reject) {
var random = Math.random()*1000;
setTimeout(
function() {
resolve(random);
}, random);
}));
}
this function will make us wait for a random time and return a Promise (if you use jQuery Promise is similar to Deferred) object. To use this function today you would write something like this
function test(){
timeoutPromise().then(function(waited){
console.log('I waited' + waited);
});
}
And this is fine. Now lets try to return the log message
function test(){
return timeoutPromise().then(function(waited){
var message = 'I waited' + waited;
console.log(message);
return message; //this is where jQuery Deferred is different then a Promise and better in my opinion
});
}
Ok this is not bad but there are two return statements and a function in the code.
Now with async this will look like this
async function test(){
var message = 'I waited' + (await timeoutPromise());
console.log(message);
return message;
}
The code is short and inline. If you written a lot of .then() or . done() you know how unreadable the code can get.
Now why the async keyword in front of function. Well this is to indicate that your return value is not what is returned. In theory you could write this(This can be done in c# I don't know if js will allow since it's not done).
async function test(wait){
if(wait == true){
return await timeoutPromise();
}
return 5;
}
you see, you return a number but the actual return will be a Promise you don't have to use
return new Promise(function(resolve, reject) { resolve(5);};
Since you can't await a number only a Promise await test(false) will throw an exception and await test(true) will not if you don't indicate async in front.
I have a situation where my WinJS app wants to call a function which may or may not be async (e.g. in one situation I need to load some data from a file (async) but at other times I can load from a cache syncronously).
Having a look through the docs I though I could wrap the conditional logic in a promise like:
A)
return new WinJS.Promise(function() { // mystuff });
or possibly use 'as' like this:
B)
return WinJS.Promise.as(function() { // mystuff });
The problem is that when I call this function, which I'm doing from the ready() function of my first page like this:
WinJS.UI.Pages.define("/pages/home/home.html", {
ready: function () {
Data.Survey.init().done(function (result) {
// do some stuff with 'result'
});
}
});
When it is written like 'A' it never hits my done() call.
Or if I call it when it's written like 'B', it executes the code inside my done() instantly, before the promise is resolved. It also looks from the value of result, that it has just been set to the content of my init() function, rather than being wrapped up in a promise.
It feels like I'm doing something quite basically wrong here, but I'm unsure where to start looking.
If it's any help, this is a slimmed down version of my init() function:
function init() {
return new WinJS.Promise(function() {
if (app.context.isFirstRun) {
app.surveyController.initialiseSurveysAsync().then(function (result) {
return new WinJS.Binding.List(result.surveys);
});
} else {
var data = app.surveyController.getSurveys();
return new WinJS.Binding.List(data);
}
});
}
Does anyone have any thoughts on this one? I don't believe the 'may or may not be async' is the issue here, I believe the promise setup isn't doing what I'd expect. Can anyone see anything obviously wrong here? Any feedback greatly appreciated.
Generally speaking, if you're doing file I/O in your full init routine, those APIs return promises themselves, in which case you want to return one of those promises or a promise from one of the .then methods.
WinJS.Promise.as, on the other hand, is meant to wrap a value in a promise. But let me explain more fully.
First, read the documentation for the WinJS.Promise constructor carefully. Like many others, you're mistakenly assuming that you just wrap a piece of code in the promise and voila! it is async. This is not the case. The function that you pass to the constructor is an initializer that receives three arguments: a completeDispatcher function, an errorDispatcher function, and a progressDispatcher function, as I like to call them.
For the promise to ever complete with success, complete with an error, or report progress, it is necessary for the rest of the code in the initializer to eventually call one of the dispatchers. These dispatchers, inside the promise, then loop through and call any complete/error/progress methods that have been given to that promise's then or done methods. Therefore, if you don't call a dispatcher at all, there is no completion, and this is exactly the behavior you're seeing.
Using WinJS.Promise.as is similar in that it wraps a value inside a promise. In your case, if you pass a function to WinJS.promise.as, what you'll get is a promise that's fulfilled with that function value as a result. You do not get async execution of the function.
To achieve async behavior you must either use setTimeout/setInterval (or the WinJS scheduler in Windows 8.1) to do async work on the UI thread, or use a web worker for a background thread and tie its completion (via a postMessage) into a promise.
Here's a complete example of creating a promise using the constructor, handling complete, error, and progress cases (as well as cancellation):
function calculateIntegerSum(max, step) {
if (max < 1 || step < 1) {
var err = new WinJS.ErrorFromName("calculateIntegerSum", "max and step must be 1 or greater");
return WinJS.Promise.wrapError(err);
}
var _cancel = false;
//The WinJS.Promise constructor's argument is a function that receives
//dispatchers for completed, error, and progress cases.
return new WinJS.Promise(function (completeDispatch, errorDispatch, progressDispatch) {
var sum = 0;
function iterate(args) {
for (var i = args.start; i < args.end; i++) {
sum += i;
};
//If for some reason there was an error, create the error with WinJS.ErrorFromName
//and pass to errorDispatch
if (false /* replace with any necessary error check -- we don’t have any here */) {
errorDispatch(new WinJS.ErrorFromName("calculateIntegerSum", "error occurred"));
}
if (i >= max) {
//Complete--dispatch results to completed handlers
completeDispatch(sum);
} else {
//Dispatch intermediate results to progress handlers
progressDispatch(sum);
//Interrupt the operation if canceled
if (!_cancel) {
setImmediate(iterate, { start: args.end, end: Math.min(args.end + step, max) });
}
}
}
setImmediate(iterate, { start: 0, end: Math.min(step, max) });
},
//Cancellation function
function () {
_cancel = true;
});
}
This comes from Appendix A ("Demystifying Promises") of my free ebook, Programming Windows Store Apps in HTML, CSS, and JavaScript, Second Edition (in preview), see http://aka.ms/BrockschmidtBook2.
You would, in your case, put your data initialization code in the place of the iterate function, and perhaps call it from within a setImmediate. I encourage you to also look at the WinJS scheduler API that would let you set the priority for the work on the UI thread.
In short, it's essential to understand that new WinJS.Promise and WinJS.Promise.as do not in themselves create async behavior, as promises themselves are just a calling convention around "results to be delivered later" that has nothing inherently to do with async.