[Technical Translation] writing asynchronous tasks in modern JavaScript,

This week again translate some technical articles, this is expected to translate three articles as follows:

I translated technical articles are placed in a github repository, if you find it useful, please click on the star collection. Why should I create this git repository? The purpose is to learn and follow-up web development by translating foreign web-related technical articles of new ideas and technologies. git repository Address: https://github.com/yzsunlei/javascript-article-translate

In this article, we will explore the evolution of the past around the asynchronous execution of JavaScript and how it is changing the way we read and write code. We will Web development, it has been an example to modern asynchronous mode.
JavaScript as a programming language has two main features, these two features are very important how our code works for understanding. The first is its synchronization feature, which means that the code will run almost line by line when you read, and secondly, it is single-threaded, at any time only execute a command.

With the development of language, the new module appears in the scene to allow for asynchronous execution. Developers try a different approach in solving more complex algorithms and data flow, leading to emerge around their new interfaces and modes.

Synchronous execution mode and observer

As mentioned in the introduction, JavaScript is usually progressive run the code you write. Even in the first few years, the language, there are exceptions, though they rarely, you may already know them: HTTP request, DOM events and time intervals.

const button = document.querySelector('button');

// observe for user interaction
button.addEventListener('click', function(e) {
  console.log('user click just happened!');
})

If you add an event listener (for example, clicking the trigger element and user interaction), the JavaScript engine will be an event listener callback task into the queue, but will continue to implement its current contents of the stack. After the completion of the call there, it will now run a callback listener.

This behavior is similar to the network request and timer happen, they are the first module Web developers access to asynchronous execution.

Although these are common JavaScript synchronous execution exception, but it is essential to understand the language still is single-threaded, and although it may Task queued asynchronous run them and then return to the main thread, but it can only be executed once a piece of code.

Our toolkit, which Alla Kholmatova explore how to create effective and maintainable design system to design outstanding digital products. Understanding Design Systems, the lessons learned from common pitfalls, traps and Alla learned over the years.

For example, let us send a network request.

var request = new XMLHttpRequest();
request.open('GET', '//some.api.at/server', true);

// observe for server response
request.onreadystatechange = function() {
  if (request.readyState === 4 && xhr.status === 200) {
    console.log(request.responseText);
  }
}

request.send();

When the server returns, the task will be assigned to the process onreadystatechangein the queue (code execution continues in the main thread).

Note: JavaScript engine to explain how the task queuing and processing thread of execution is a very complex subject, it may be worth reading. However, I recommend that you view the " event loop in the end is what? " Help Philip Roberts (Phillip Roberts) offers to help you better understand.

In each of these cases, we are responding to external events. A certain time interval, in response to user operation or the server. We alone can not create an asynchronous tasks, we always observe the events happening beyond our scope.

This is why this type of template code is called the "observer mode", addEventListenerin this case, it may be better represented by the interface. Soon, events, libraries or frameworks exposure of this model to flourish.

NODE.JS and event triggers

A good example is Node.js, the page describes himself as "event-driven asynchronous JavaScript runtime," so the event triggers and callbacks are first-class citizens. It is even used EventEmitterhas achieved a constructor.

const EventEmitter = require('events');
const emitter = new EventEmitter();

// respond to events
emitter.on('greeting', (message) => console.log(message));

// send events
emitter.emit('greeting', 'Hi there!');

This is not only universal asynchronous method execution, but also the core of practice mode and its ecosystem. Node.js has opened up a new era even write JavaScript outside the network in different environments. As a result, other asynchronous case is also possible, for example, create a new directory or write to a file.

const { mkdir, writeFile } = require('fs');

const styles = 'body { background: #ffdead; }';

mkdir('./assets/', (error) => {
  if (!error) {
    writeFile('assets/main.css', styles, 'utf-8', (error) => {
      if (!error) console.log('stylesheet created');
    })
  }
})

You may notice that the first parameter error callback function is, if a response is required data, it is as the second parameter. This is called "priority error correction mode", it is a convention authors and contributors for their own packages and libraries used.

Promise and endless chain of callbacks

As Web developers face more complex problems to be solved, the demand for better asynchronous work appeared. If we look at the last code fragment, we will see a repeat of the callback chain, with the increasing number of tasks, expand the callback chain effect on the poor.

For example, let's add only two steps, namely pre-file reads and style.

const { mkdir, writeFile, readFile } = require('fs');
const less = require('less')

readFile('./main.less', 'utf-8', (error, data) => {
  if (error) throw error
  less.render(data, (lessError, output) => {
    if (lessError) throw lessError
    mkdir('./assets/', (dirError) => {
      if (dirError) throw dirError
      writeFile('assets/main.css', output.css, 'utf-8', (writeError) => {
        if (writeError) throw writeError
        console.log('stylesheet created');
      })
    })
  })
})

We can see that because multiple callbacks chain and repeated error handling, with the program being developed become more complex, the code becomes more difficult known.

Promise, packaging and chain model

Promises originally announced when they are the new features of the JavaScript language, and did not cause too much attention, they are not a new concept, because other languages ​​decades ago has achieved a similar function. The fact is that since the emergence, they found that semantic structure and I did most of the projects have changed a lot.

Promises not only introduced for developers to write asynchronous code built-in solutions, but also to build new features for Web developers (such as Web specification) of the base to open a new phase of fetch Web development.

Migrating from a callback method to method-based Promise becoming more common in the project (such as libraries and browser), the Node.js even began to slowly migrate to them.

For example, a package of about Node readFile method:

const { readFile } = require('fs');

const asyncReadFile = (path, options) => {
  return new Promise((resolve, reject) => {
    readFile(path, options, (error, data) => {
      if (error) reject(error);
      else resolve(data);
    })
  });
}

Here we are, resolve and reject calls when defining error object when the method results in the successful implementation of Promise by the constructor, to cover up the callback.

When a method returns a Promise objects, we can pass to a function to parse the then follow its success, the parameters are parsed Promise value, in this case is data.

If you catch an error is raised during the process, the function (if present) is called.

Note: If you need more in-depth understanding of the work Promises, I suggest Jake Archibald wrote on the Google Web development blog "JavaScript Promises: Introduction" article.

Now we can use these new methods and avoid callback chain.

asyncRead('./main.less', 'utf-8')
  .then(data => console.log('file content', data))
  .catch(error => console.error('something went wrong', error))

It has methods to create asynchronous tasks and clear interface to track its possible outcomes, so that the industry out of the Observer pattern. Based on Promise of code seems to solve the code unreadable and error-prone.

With better or clearer syntax error message is highlighted in the coding help, for developers, the code is easier reasoning becomes more predictable, and the implementation path better and easier capture possible code trap.

Promises Due to the high degree of popularity in the community, Node.js quickly released a version of its built-in I / O method to return Promise objects, such as import files from operations fs.promises.

It even provides a promisify utility package for all functions follow priority error correction mode, and convert it to a function based on the Promise.

However, Promises can help you in all cases?

Let us re-imagine the pre-mission style with Promises written.

const { mkdir, writeFile, readFile } = require('fs').promises;
const less = require('less')

readFile('./main.less', 'utf-8')
  .then(less.render)
  .then(result =>
    mkdir('./assets')
      .then(writeFile('assets/main.css', result.css, 'utf-8'))
  )
  .catch(error => console.error(error))

Redundant code is significantly reduced, especially in the catch error handling we now depend, to some extent, but failed to provide clear Promises code directly related to the operation of a series of indentation.

This is actually implemented on the first statement after the call then readFile. What happens after these lines is the need to create a new scope, we can create a directory first in the scope, and the result is written to the file. This leads to the indentation rhythm disruption, at first glance it is difficult to determine the sequence of instructions.

One way to solve this problem is to pre-process custom to this problem, and allow the proper connection method, but we will have the code seems to have achieved the desired function of the task to introduce more complexity.

Note: This is a sample program, we can control some of the methods that follow industry practice, but not always. With different types of libraries or through the introduction of more complex series, we can easily break the code style.

Happily, JavaScript community again from another language grammar learned something, and added a notation that can help asynchronous tasks in series instead of pleasing or as straightforward as synchronization code in many cases.

async和await

A Promise when executed is defined as a value unresolved, creating an instance of a Promise is an explicit call to the module.

const { mkdir, writeFile, readFile } = require('fs').promises;
const less = require('less')

readFile('./main.less', 'utf-8')
  .then(less.render)
  .then(result =>
    mkdir('./assets')
      .then(writeFile('assets/main.css', result.css, 'utf-8'))
  )
  .catch(error => console.error(error))

In the internal async method we can use to determine a reserved word await resolution, Promise and then continue.

Let's use this syntax to access or re-write the code segment.

const { mkdir, writeFile, readFile } = require('fs').promises;
const less = require('less')

async function processLess() {
  const content = await readFile('./main.less', 'utf-8')
  const result = await less.render(content)
  await mkdir('./assets')
  await writeFile('assets/main.css', result.css, 'utf-8')
}

processLess()

Note: Please note that because we can not use asynchronous functions outside the scope of today, so you need to move all the code method await.

Every async method to find an await statement, it will stop execution until the process value or Promise is resolved so far.

Although asynchronous execution, but using async / await indicate there will be significant consequences for law, the code looks like async, it is our view and developers more accustomed to reasoning.

Error handling it? To do this, we use has been around a long sentence in the language, try and catch.

const { mkdir, writeFile, readFile } = require('fs').promises;
const less = require('less');

async function processLess() {
  try {
    const content = await readFile('./main.less', 'utf-8')
    const result = await less.render(content)
    await mkdir('./assets')
    await writeFile('assets/main.css', result.css, 'utf-8')
  } catch(e) {
    console.error(e)
  }
}

processLess()

Rest assured that any error caused in the process will be processed within the code statements that catch. We are responsible for error handling in a central position, but now we have an easy to read and follow the code.

Subsequent operation having no need to store return values ​​mkdir not damage the code in the variable rhythm; and there is no need to create a new scope in subsequent steps to access the value of the result.

It's safe to say, Promises is a basic module of the language introduced in JavaScript is enabled for async/awaitrepresentation is required, you can use it in a modern browser and the latest version of Node.js in.

Note: in recent JSConf, Node founder and first contributor Ryan Dahl regret not insist Promises of early development, mainly because the Node goal is to create an event-driven server and file management, and Observer mode is more suitable for this purpose .

in conclusion

The purpose of introducing Promises Web development world is changing the way we line up operations in the code, and changed our way of instruction execution reasoning and the way we write libraries and packages.

But to get rid of the callback chain is difficult to solve, I think then after the observer mode and method used for many years a leading provider of used, have to go through a method does not help us get rid of the idea. Such a community like Node.js.

As Nolan Lawson (Nolan Lawson) excellent article about it in the wrong Promise series used in the earlier, the callback old habits will die! Later, he explained how to avoid these traps.

I think Promises is an intermediate step, which allows a natural way to generate asynchronous tasks, but it did not help us to further improve better code mode, sometimes you actually need more adapted and improved language syntax.

When we try to use JavaScript to solve more complex problems, we see the need for a more mature language, and tried not previously been seen on the network architecture and patterns.

We still do not know how to behave ECMAScript standard, because we have been the JavaScript governance extend beyond the network, and try to solve more complex problems.

It is difficult to say that we really need to change the language from these challenges into something simpler procedures need, but how do I promote things on the Web and JavaScript itself, trying to adapt to new environmental challenges and satisfied. Compared to start writing the code in the browser a decade ago, and now I think JavaScript is a more friendly place asynchronously.

Original link: https://www.smashingmagazine.com/2019/10/asynchronous-tasks-modern-javascript/

Guess you like

Origin www.cnblogs.com/yzsunlei/p/12189960.html