Let me show you a quick comparison of JavaScript Promises and Python futures, where I can point out the main features and show the reasons for the solutions.
I will use the following dummy example to demonstrate the use of asynchronous functions:
async function concatNamesById(id1, id2) { return (await getNameById(id1)) + ', ' + (await getNameById(id2)); }
Asynchronous JavaScript
On the same day, before the advent of the Promises concept, people wrote down their code using callbacks. There are various conventions as to what argument the callback should be, how to handle errors, etc. At the end, our function looks something like this:
This does the same as in the example, and yes, I used 4 spaces indented intentionally to increase the problem with the so-called hell callback or death pyramid . People using JavaScript have been writing code for many years!
Chris Koval then came with his flaming Q library and saved the frustrated JavaScript community by introducing the concept of Promises. The name is intentionally not "future" or "task." The main goal of the Promise concept is to get rid of the pyramid. To achieve this, promises have a then method, which not only allows you to subscribe to an event that will be fired when the promised value is received, but will also return another promise, allowing the chain. This is what makes Promises and Futures another concept. Promises a little more.
Cm? You must deploy the Promises returned from then callbacks to create a clean, transparent chain. (I myself wrote such asynchronous code for more than a year.) However, it gets complicated when you need some control flow, for example, conditional branches or loops. When the first compilers / transpilers (like 6to5) for ES6 appeared, people slowly started using generators. ES6 generators are two directional, which means the generator not only produces values, but can also receive setpoints at each iteration . This allowed us to write the following code:
Still using Promises, Q.async makes an asynchronous function from the generator. There is no black magic, this wrapper function is implemented using nothing but promise.then (more or less). We are almost there.
Today, since the ES7 specification for asynchronous wait is pretty mature, anyone can compile asynchronous ES7 code for ES5 using BabelJS .
Return promise from an asynchronous function
So this works:
async foo() { return sleep('bar', 1000);
so:
async foo() { return await await await 'bar';
This type of weak / dynamic / duck typing is well suited for viewing in the world around the world.
You are right that you can return a promise from an asynchronous function without waiting, and it breaks. This is not a solution, but a direct consequence of how promise.then works, as it promises to make the chain convenient. However, I think it is good practice to write a wait before each asynchronous call, to make sure that you know that the call is asynchronous. . We have several errors every day due to the lack of pending keywords, because they will not cause instant errors, just a bunch of random tasks running in parallel. I like to debug them. Really.
Asynchronous Python
See what Python people did before asynchronous expectations coroutines were added in python:
def concatNamesById(id1, id2): return getNameById(id1) + ', ' + getNameById(id2);
Wait what? Where are the futures? Where is the callback pyramid? The thing is, Python people do not have the problems people have encountered in JavaScript. They just use blocking calls.
Big difference between JavaScript and Python
So why didn't JavaScript users use blocking calls? Because they could not! Well, they wanted to. Believe me. Before they introduced WebWorkers , all of the JavaScript code worked in the gui thread, and any blocking call caused ui to freeze! This is undesirable, so the people who wrote the specifications did everything to prevent such things. Today, the only way I can block the user interface flow in a browser that I know of:
- Using XMLHttpRequest with deprecated
async = false option - Using Spin Waiting
- (Or do real heavy calculations)
Currently, you cannot implement spinlocks and similar things in JavaScript, there is simply no way. (Until browser developers start implementing things like Shared Array Buffers , which I'm afraid will bring a special kind of hell as soon as enthusiast enthusiasts start using them)
On the other hand, there is nothing wrong with blocking calls in Python, since there is usually no such thing as a 'gui thread'. If you still need a little parallelism, you can start a new thread and work on it. This is useful when you want to run multiple SOAP requests at once, but not so useful when you want to use the processing power of all the processor cores in your laptop, as Global Interpreter Lock will prevent you from doing this. (This was worked on multiprocessing , but that's a different story)
So why do Python people need coroutines? The main answer is that reactive programming has become really popular these days. Of course, there are other aspects, such as the reluctance to start a new thread for each of your quiet requests (some Python libraries are known to skip thread IDs until they ultimately fail) or just want to get rid of all unnecessary multi-threaded primitives, such as mutexes and semaphores. (I mean, these primitives can be omitted if the code can be rewritten into coroutines. Of course, they are necessary when you are doing real multithreading.) And that is why futures were developed.
Python futures do not allow chaining in any form. They are not intended to be used in this way. Remember, JavaScript promises had to change the pyramid scheme to a beautiful chain scheme, so you had to unwind it. But automatic for unwinding requires specific code for writing and needs a future resolution in order to distinguish between the set values according to their types or properties. That is, it would be more complex (more difficult to debug) and would be a small step towards weak typing, which contradicts the basic principles of python. Python futures are easy, clean and straightforward. They just do not need automatic unwinding.