Although we live in an asynchronous world, asynchrony is unfamiliar to most beginners in programming. Learning a programming language usually starts with synchronous flow: order, branch, and loop. What is an asynchronous process? Start an asynchronous call, and then… There is no next. Where did the asynchronous program go?

Asynchronous programs will run in some form of asynchrony, such as multithreading, asynchronous IO, etc., until the processing is complete. What if you need to process the results? You give a program an entry point, tell it to process the current process, send the result to that entry point, and then execute another program, commonly known as a callback. Callbacks are usually called callback, but sometimes I prefer next because it represents the next processing step.

The concept of synchronization and asynchrony

Now we have some concepts like synchronous and asynchronous, what are they?

These concepts do not come from programming languages, but from low-level instructions, and even lower-level circuits. They are two concepts based on timing, where “step” refers to pace, so synchronous means the same pace and asynchronous means different pace. Of course, when the two concepts were elevated to the procedural level, the precise meaning was irrelevant to the clock, but the meaning remained the same.

synchronous

Take a real life example to illustrate the problem — rule out buying tickets. The ticket office opened a window and there was a queue of people waiting to buy their tickets. In this procession, the person in front takes a step forward, the person behind can take a step forward; In front of the people waiting, behind the people must be waiting. So in an ideal world, everyone could step forward at the same time. OK, everybody in sync, that’s called synchronization.

Here, the ticket window is regarded as the processor, and each person is regarded as the instruction waiting to be executed. The action of buying a ticket is the execution of the instruction. It is characterized by a step-by-step schedule, which can cause congestion if one person takes too long to buy a ticket (the instruction takes too long to execute).

Asynchronous (multi-threaded)

Now more and more people buy tickets, so the ticket office opened several Windows at the same time. Each individual team is still in sync, but the different teams are no longer in sync, called asynchrony. The ticket sale in queue A was very smooth, and the queue was moving in an orderly and fast way. However, A customer in queue B seemed to have some trouble in paying, which took A long time and caused congestion, but this did not affect queue A.

At this time the ticket office can be seen as running asynchronous programs in a multithreaded manner. From this example, you can see two characteristics of asyncrony: first, two asynchronous processes are independent of each other and do not block each other (provided there is no need to wait for shared resources); Second, asynchronous programs are still synchronized internally.

Asynchronous (IO)

The above example is more suitable for multi-threaded asynchrony. What about IO asynchrony?

At the end of the year, M is preparing the materials for the year-end report, which is a stressful job (CPU), and he needs to collect a lot of data to write a lot of documents. For one copy, M needed production data from the workshop, but a trip to the workshop (IO) would take a lot of time, so he sent N to the workshop to collect data, while he continued to write other proposals, waiting for N to collect data back (start asynchronous program). Half a day later, N brings back the data (inserts the event message), M continues to finish the copy (completes the current event loop), and then uses the data brought back by N to write the report about the workshop (new event loop)…

IO processing is much slower than CPU processing, so ASYNchrony saves the CPU from sitting idle waiting for an I/O operation to complete. When the I/O operation is complete, the CPU uses the result of the I/O operation to continue working.

Synchronous logic and asynchronous logic

Back to the program, we describe synchronous and asynchronous processing as a function.

Synchronization logic

So, the synchronization process is:

Receiving an input tail processing an output tailCopy the code

It’s described by a piece of pseudocode

Note: The pseudocode in this article is close to JavaScript syntax, although TypeScript type declaration syntax is sometimes used to illustrate types.

function func(input) {
    do something with input
    return output
}
Copy the code

This is standard INput-process-output IPO processing.

Asynchronous logic

And asynchrony is:

Accept input tail handle tail start next step (if any)Copy the code

To describe it in pseudocode:

function asyncFunc(input, next) {
    do something with input
    if (next is a entry) {
        next(output)
    }
}
Copy the code

This Process is called IPN(input-process-next).

Notice Next here, Next, only one step. This step covers the following steps. So this step can only be the subsequent steps to encapsulate a module entry, or function.

Therefore, modularity is a key idea in asynchronous thinking. Many beginner code writers tend to write down hundreds or thousands of lines of functions like a running list, which is a lack of modular thinking. Modularization requires training, analyzing code dependencies, refining functions, extracting objects, and mastering the granularity balance of module refinement after some experience. It can’t be done overnight, but I recommend reading books on design patterns and refactoring.

Asynchronous development tools (SDK and syntactic level)

Make a Promise

Consider the annual report example above. When M asks N to go to the workshop to collect data, N says, “OK, I’ll bring the data back soon”, which is a promise. Based on this commitment, M can arrange to write the report material about the workshop later. This process is described in pseudocode

functionCollectData (): Promise {// N goes to collectData, making a PromisereturnNew Promise(resolve => {collect data from workshop // this Promise will eventually bring data resolve(data)})}functionWriteWorkshopReport (data) {write report with data} CollectData (). Then (data => writeWorkshopReport(data))Copy the code

Some language SDKS, notably JavaScript, use Promise. ContinueWith and Task

.ContinueWith are used instead of promise.then.

Asynchronous logical synchronization

The difference between synchronous and asynchronous thinking in a processing step was mentioned above. If you jump out of a processing step, from the perspective of the larger processing process, asynchronous and synchronous actually not much different, are input -> processing -> produce output -> output for the next step, the only attention is to wait for the output generated by asynchronous processing, we can call it asynchronous wait. Since we can do something else while async wait (short await), this wait does not block. However, because the wait is declared, the compiler/interpreter automatically calls subsequent code after the wait is complete, making asynchronous code write just like synchronous code.

The above example using asynchronous wait pseudocode would look something like this

async functionCollectData (): Promise {collect data from workshop // Most languages encapsulate the return value of async functions as a Promisereturn data
}

functionWriteWorkshopReport (data) {write report with data} // await can only be used with async in functions declared asyncfunction main() {data = await collectData() writeWorkshopReport(data)}Copy the code

Languages such as C# and JavaScript syntactically state that ‘await’ must be used in functions declared async. This defines the use of ‘await’ at compile/interpret level. As long as’ await ‘is used, it must be in an asynchronous context. Async also requires the compiler/interpreter to perform some automatic processing on its return value. For example, in JavaScript, if its return value is not a Promise object, it will automatically wrap a Promise object. In C#, it is automatically wrapped as Task or Task

(so async methods need to be declared as Task or Task

).

Attention, attention, attention

Although language services have done a lot to synchronize asynchronous programs, there are still some human errors, such as forgetting to write the await keyword. In strongly typed languages the compiler checks more closely, but in JavaScript, forgetting to write await means that a statement that was supposed to get a value gets a Promise. The interpreter will not question this, but the program will run incorrectly.

summary

In general, asynchronous programming is not particularly difficult. Using the async/await language feature it is even possible to write asynchronous code in a similar way to writing synchronous code. But syntactic sugar is sugar, and learning asynchronous programming well requires understanding and familiarity with async, callbacks, promises, modularity, design patterns, refactoring, and more.

reading

  • Step from a small topic to asynchronous JavaScript calls
  • Chat asynchronous call “flat”
  • Understand JavaScript async/await
  • C# Parallel computing (Parallel and ParallelQuery)