Promise

In short, Promises can handle asynchronous programs.

The callback hell

JS, or in the node, a large number of the use of the callback function for the asynchronous operation, when the asynchronous operation will return the result is not controlled, if we hope for an asynchronous request according to the order to perform, you will need to put these asynchronous operations nested, nested layers much more special, can form the callback hell Or lateral pyramid.

The following example has the meaning of callback hell:

Example: there are three files a.txt, b.txt, c.txt, using fs template to read the contents in order, code:

// Read the contents of a, b, c in order to output
import fs from 'fs';

// Read a file
fs.readFile('./a.txt'.'utf-8'.(err, data) = > {
    if (err) throw err;
    console.log(data);
    // Read the b file
    fs.readFile('./b.txt'.'utf-8'.(err, data) = > {
        if (err) throw err;
        console.log(data);
        // Read the c file
        fs.readFile('./c.txt'.'utf-8'.(err, data) = > {
            if (err) throw err;
            console.log(data);
        });
    });
});
Copy the code

In the example, there are only three files, and if there are a lot of files that need to be read sequentially, the amount of nested code will be terrible, which is what callback hell means. Here’s the solution

Encapsulates a function that reads files asynchronouslyfunction route(filePath) {
    const p = new Promise((resolve, reject) = > {
        fs.readFile(filePath, 'utf-8'.(err, data) = > {
            // data is the content of the imported file
            resolve(data)
        })
    })
    return p
}

route('./data/1.txt')
    .then(res= > {
        console.log(res);
        return route('./data/2.txt')
    })
    .then(res= > {
        console.log(res);
        return route('./data/3.txt')
    })
    .then(res= > {
        console.log(res);
    })
Copy the code

So you can see at a glance where you need to change can be found immediately!

Introduction of Promise

  • Promise objects can solve the problem of callback hell
  • Promise is a solution to asynchronous programming that is more reasonable and powerful than traditional solutions (callback functions and events)
  • Promise can be understood as a container in which to code asynchronous programs
  • Syntactically, a Promise is an object that needs to be usednew

Promise simple use

A Promise stands for “Promise.” In this case, an asynchronous operation within a Promise is a Promise that either fulfills its Promise or fails.

So, with promises, there are two main parts: first there is a Promise (an asynchronous operation) and then the result is fulfilled.

Part 1: Defining “Commitment”

// Instantiate a Promise, which defines a container and passes it a function that takes two parameters, usually resolve and reject. This function can write asynchronous request code
// On the other hand, it can also be interpreted as making a commitment
let p = new Promise((resolve, reject) = > {
    // the resolve parameter means done
    The parameter reject means reject
    fs.readFile('./a.txt'.'utf-8'.(err, data) = > {
        
        if (err) {
            // Fail, tell others, promise failed
            reject(err);
        } else {
            // Success, tell others, promise fulfilledresolve(data); }}); });Copy the code

Part two: Getting the results of the promise

// The result of the above "promise" can be obtained by calling the then method of p
The then method takes arguments of two function types, argument 1 representing the function to be called when the promise succeeds, and optional argument 2 representing the function to be executed when the promise fails
p.then(
	(data) = > {},
  (err) = >{});Copy the code

Complete code:

import fs from 'fs';/ / promise promise

// Use Promise in two parts

1. Define a commitment
let p = new Promise((resolve, reject) = > {
    // resolve -- resolve; It's a function
    // reject - to reject or fail; It's a function
    // Code for asynchronous operations, which is a promise
    fs.readFile('./a.txt'.'utf-8'.(err, data) = > {
        if (err) {
            reject(err);
        } else{ resolve(data); }}); });// 2. Keep your promise
// p.then(
// (data) => {}, // a function-like parameter used to obtain the data after the promise is successful
// (err) => {} // Parameter of function type, used for error message after or promise failure
// );
p.then(
    (data) = > {
        console.log(data);
    },
    (err) = > {
        console.log(err); });Copy the code

Three states

  • Initial state: Pending, where the result of a promise is undefined;
  • This is a big pity. When resolve(value) is called, one of the final states is fulfilled: fulfilled, and the result value can be obtained
  • Reject (error) : reject(error); reject(error) : reject(error)

This is a big pity or Rejected, and the promise will not change again.

The characteristics of

When resolve is called, the Promise reaches its final state. Once the final state is reached, the Promise state stays the same.

The first call to resolve is valid.

const fs = require('fs');

// 1. Create a Promise object; (promise)
let p = new Promise((resolve, reject) = > {
  resolve(123);
  resolve(456); // This call is invalid
});

// 2. Obtain the result of the asynchronous task
// p.chen (function 1, [function 2]);
p.then(res= > {
  console.log(res); / / 123
}, err= > {
  console.log(err);
});
Copy the code

Synchronous asynchronous?

  • New Promises are executed synchronously
  • When the result is retrieved (when the resolve trigger then method is called), it is asynchronous
console.log(1);

new Promise((resolve, reject) = > {
  console.log(2);
  resolve();
  console.log(3);
}).then(res= > {
  console.log(4); // This is asynchronous
})

console.log(5);

// Output order: 1, 2, 3, 5, 4, because only.then() is asynchronous
Copy the code

Chain calls to the then method

  • The string returned from the previous THEN is received by the next THEN method. But there’s no point;

  • The Promise object returned from the previous THEN, and the call to resolve passes data that will be received by the next THEN

  • If resolve is not called from the previous THEN, the subsequent THEN will receive no value

    const fs = require('fs');
    / / promise promise
    
    let p1 = new Promise((resolve, reject) = > {
      fs.readFile('./a.txt'.'utf-8'.(err, data) = > {
        err ? reject(err) : resolve(data.length);
      });
    });
    
    let p2 = new Promise((resolve, reject) = > {
      fs.readFile('./b.txt'.'utf-8'.(err, data) = > {
        err ? reject(err) : resolve(data.length);
      });
    });
    
    let p3 = new Promise((resolve, reject) = > {
      fs.readFile('./c.txt'.'utf-8'.(err, data) = > {
        err ? reject(err) : resolve(data.length);
      });
    });
    
    p1.then(a= > {
      console.log(a);
      return p2;
    }).then(b= > {
      console.log(b);
      return p3;
    }).then(c= > {
      console.log(c)
    }).catch((err) = > {
      console.log(err);
    });
    Copy the code

    The catch method can uniformly get error information

Use a third-party module to read files

  • npm init – y
  • NPM I THEN-fs Installs the THEN-FS module
  • Then-fs encapsulates the built-in FS module and returns a Promise object after reading the file, eliminating our own encapsulation
// The then-fs module is a third-party module that requires NPM to install then-fs
const fs = require('then-fs');

// then-fs encapsulates the built-in FS module rewrite. After the method is called, the Promise object is returned

let p1 = fs.readFile('./files/a.txt'.'utf-8');  // 
let p2 = fs.readFile('./files/b.txt'.'utf-8');
let p3 = fs.readFile('./files/c.txt'.'utf-8');

// Get the result from then
p1.then(res= > {
  console.log(res.length);
  return p2;
}).then(res= > {
  console.log(res.length);
  return p3;
}).then(res= > {
  console.log(res.length);
})
Copy the code

Async and await modifiers

ES6 — ES2015

Async and await are proposed in ES2017.

Asynchronous manipulation is the bane of JavaScript programming, so much so that various solutions have been proposed to solve this problem.

From the earliest callback functions, to the Promise object, to the Generator, each time improved, but felt incomplete. They all have an additional level of complexity that requires an understanding of the underlying mechanics of abstraction.

Asynchronous I/O is just reading a file. Why make it so complicated? The ultimate state of asynchronous programming is that you don’t care if it’s asynchronous at all.

The ==async function is the light at the end of the tunnel and is considered by many to be the ultimate solution for asynchronous operations.

ES2017 provides the async and await keywords. Await and async keywords can return the result of an asynchronous request to us as a return value.

  • Async decorates a function
    • Async – modified function that always returns a Promise object
    • The return value within the function will be wrapped automatically in the Resolved promise
  • Await can only appear inside async functions
    • Await makes the JS engine wait until the promise completes and returns the result
    • Syntax: let value = await promise object; // Wait for the promise object to complete before you get the result
    • Since await needs to wait for the promise to complete, await suspends the execution of the function, but does not affect other synchronization tasks
  • For error handling, you can optionally use it after async functions.catch()Or used after a Promise object.catch()
const fs = require('fs');
// Encapsulate the code that reads the file asynchronously
function myReadFile (path) {
    return new Promise((resolve, reject) = > {
        fs.readFile(path, 'utf-8'.(err, data) = > {
            err ? reject(err) : resolve(data.length);
        });
    }).catch(err= > {
        console.log(err);
    });
}

async function abc () {
    let a = await myReadFile('./a.txt');
    let b = await myReadFile('./b.txt');
    let c = await myReadFile('./c.txt');
    console.log(b);
    console.log(a);
    console.log(c);
}

abc();
Copy the code

Error handling

The premise is that you get a couple of Promise objects that look like this:

let fs = require('then-fs');

let p1 = fs.readFile('./files/a.txt'.'utf-8');   
let p2 = fs.readFile('./files/bbb.txt'.'utf-8');  // Note that the path is intentionally wrong
let p3 = fs.readFile('./files/c.txt'.'utf-8');
Copy the code

The result of getting a Promise can be obtained via then. It can also be obtained with async and await.

If you use then to get results, how do you handle the error: just add a catch method to the end of the chain call

/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the results obtained through then -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
p1.then(res= > {
  console.log(res.length);
  return p2;
}).then(res= > {
  console.log(res.length);
  return p3;
}).then(res= > {
  console.log(res.length);
}).catch(err= > {
  console.log(err);
})
Copy the code

If you use async and await to get results, the best error handling is to use try… catch …

/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- through the async/await to get the result -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
async function abc() {
  try { // Try to do something
    let r1 = await p1; // Get the result normally
    let r2 = await p2; // If there is an error, throw err is thrown.
    let r3 = await p3;
    console.log(r1.length, r2.length, r3.length);
  } catch (e) {
    console.log(e); // In this case, the error thrown in the previous try is caught
  }
}
abc();
Copy the code

summary

  • Promises are a solution to asynchronous tasks
  • Get the Promise object
    • His new Promise ()
    • Encapsulate the function itself, which returns a Promise object
    • Use third-party modules or libraries. Like then-FS, like Axios
  • To get the results
    • Get by the then method, and catch the error message
    • Get the result with async and await and try… catch… Capture the error

Macro and micro tasks, event loops

JavaScript is single-threaded, which means that JavaScript can only perform one task at a time, and other tasks have to wait.

Why is JavaScript single threaded

Js is a scripting language that runs in a browser, because it often involves manipulating the DOM. If it is multithreaded, it means that multiple tasks can be performed at the same time.

Imagine if one thread modifies the DOM and another deletes it, and the browser doesn’t know which operation to perform first.

So js will execute task by task.

Why are tasks divided into synchronous and asynchronous tasks

Imagine if js tasks were synchronous, what would happen to delayed tasks such as timers, network requests, etc.?

Pages can crash and need to be paused to wait for code that takes a long time to complete

So, asynchronous tasks were introduced.

  • Synchronization task: A synchronization task can be performed immediately without waiting, such as Console
  • Asynchronous tasks: Asynchronous tasks need to wait a certain amount of time to see the result, such as setTimeout, network request

Event Loop

The event loop is simpler. It is an infinite loop that transitions between “JavaScript engine waiting for task “, “executing task”, and “going to sleep for more tasks”.

The general algorithm of the engine:

  1. When there is a task:
    • Start with the task that comes in first.
  2. With no other tasks, sleep until a task appears, then go to Step 1.

Task queue

According to the specification, event loops are coordinated through the mechanism of task queues. An Event Loop can have one or more task queues. A task queue is a collection of ordered tasks. Each task has a task source. Tasks from the same task source must be added to the same task queue. Tasks from different sources are added to different queues. Apis such as setTimeout/Promise are mission sources.

The key steps for each iteration of the event loop are as follows:

  • Select the oldest task in the loop and execute it if there is one
  • Check for Microtasks and continue executing until the Microtasks Queue is empty.
  • Update render (DOM render)
  • The above is a loop, and the main thread repeats the above steps

A few things to know about this cycle:

  • JS is divided into synchronous tasks and asynchronous tasks
  • Synchronization tasks are executed on the main thread, forming an execution stack
  • Outside of the main thread, the host environment manages a task queue and places an event in the task queue whenever an asynchronous task has run results.
  • Once all synchronous tasks in the execution stack are completed (the JS engine is idle at this time), the system reads the task queue, adds runnable asynchronous tasks to the executable stack, and starts execution.

Macro task

(Macro)task: The code that executes on each stack is a macro task (including fetching an event callback from the event queue and placing it on the execution stack).

Task (code) Macro task The environment
script Macro task The browser
The event Macro task The browser
Web request (Ajax) Macro task The browser
SetTimeout () timer Macro task Browser/Node
Fs.readfile () reads the file Macro task Node

For example, when you go to the bank to queue for business, everyone’s business is a macro task.

Micro tasks

A microtask is a part of a macro task that is executed after synchronous code execution and before the next macro task.

Microtasks include:
Promise.then process.nexttick (node.js environment)Copy the code

For example, a person goes to the bank to deposit money. After saving money, he or she performs some operations, such as buying commemorative coins, buying financial products and applying for credit cards. These are called microtasks.

Operation mechanism

In an event cycle, each operation is called tick. The task processing model of each tick is complex, but the key steps are as follows:

  • Execute a macro task (fetch from event queue if not in execution stack)
  • If a microtask is encountered during execution, it is added to the task queue of the microtask
  • Execute all the microtasks in the current microtask queue (in sequence) immediately after the synchronization code in the macro task completes.
  • When the macro task completes, the render is checked, and the GUI thread takes over
  • After rendering, the JS thread takes over and starts the next macro task (fetched from the event queue)

I’ll finish off with a few interview questions for you

  • 1
console.log(1)

setTimeout(function() {
  console.log(2)},0)

const p = new Promise((resolve, reject) = > {
  resolve(1000)
})
p.then(data= > {
  console.log(data)
})

console.log(3)
Copy the code
  • 2
console.log(1)
setTimeout(function() {
  console.log(2)
  new Promise(function(resolve) {
    console.log(3)
    resolve()
  }).then(function() {
    console.log(4)})})new Promise(function(resolve) {
  console.log(5)
  resolve()
}).then(function() {
  console.log(6)})setTimeout(function() {
  console.log(7)
  new Promise(function(resolve) {
    console.log(8)
    resolve()
  }).then(function() {
    console.log(9)})})console.log(10)
Copy the code
  • 3
  console.log(1)

  setTimeout(function() {
    console.log(2)},0)

  const p = new Promise((resolve, reject) = > {
    console.log(3)
    resolve(1000) // mark as successful
    console.log(4)
  })

  p.then(data= > {
    console.log(data)
  })

  console.log(5)
Copy the code

Problem one prints 1, 3, 1000, 2

1 5 10 6 2 3 4 7 8 9

Problem 3:1, 3, 4, 5, 1000, 2