The overview

  • Asynchrony: The time gap between now and the future
  • Parallelism: Something that can happen simultaneously

Asynchronous programming requirements: JavaScript programs in browsers typically have event drivers, while javascript-based servers typically wait for the client to send a network request before executing the program

Asynchronous events include: callback functions (errors cannot be caught by the initiator of asynchronous operations, the concatenation of multiple asynchronous operations requires deeply nested callback functions, creating callback hell), event listener addEventListener/ ON (event-driven, code execution process is not clear), the optimized version of event listener “publish/subscribe mode”.

The basic callback function ES6 has a new promise that standardizes asynchronous error handling through a contract chain. ES2017 async and await, write asynchronous code in synchronous form ES2018 asynchronous iterators and for/await loops, which operate asynchronous event streams in simple loops that look synchronous.

Understand synchronous asynchrony, blocking, and non-blocking from an OS perspective

Asynchrony corresponds to synchronization, which describes the relationship between threads. The two threads are either synchronous or asynchronous.

Blocking and non-blocking are threads that are either blocked or non-blocked at some point in time for the same thread.

Relational blocking is the result of synchronous and non-blocking use of asynchronous mechanisms

basis

JavaScript is single-threaded, that is, there is only one main thread when JavaScript is executed, and its host environment (Node, browser) is multi-threaded. The main thread is a single thread on which all synchronization tasks are executed, forming an execution Context stack. All blocks are handed over to a thread pool, and the main thread collaborates with the pool through a transaction queue. Non-blocking: When the main thread executes an asynchronous task, the main thread will pending the task. When the asynchronous task completes, it will be placed in the asynchronous task queue. Asynchronous task queues are executed after all tasks in the current execution stack are completed.

Event-based loop: The execution of the main thread is a tick(synchronous, microtask, macro task) that repeats

There are actually two types of asynchronous task queues, micro tasks (first) and macro tasks (last)

Single-threaded execution means that the browser stops responding to user input during script and event handler execution, so it is the responsibility of the JavaScript programmer to ensure that JavaScript scripts and time handlers do not run for long. If the script performs computationally heavy tasks, it can cause document loading delays. So asynchronous or web worker multithreading is necessary

promise

A Promise is a stateful object that can get messages for asynchronous operations, and state transitions are irrevocable

  • Pendding is asking when a thing is given to Promise
  • Will be fulfilled successfully
  • The rejected failure
var myFirstPromise = new Promise(function(resolve, reject){
    Resolve (...) is called when the asynchronous code executes successfully. Reject (...) is called when asynchronous code fails.
    // In this case, we use setTimeout(...) To simulate asynchronous code, which could be an XHR request or some HTML5 API method.
    setTimeout(function(){
        resolve("Success!"); // The code works fine!
    }, 250);
});

myFirstPromise.then(function(successMessage){
    //successMessage values resolve(...) The value passed by the method.
    // The successMessage parameter doesn't have to be a string, but this is just an example
    document.write("Yay! " + successMessage);
});
Copy the code

New Promise() is passed as a function argument that will be executed immediately by synchronization. In practice, an asynchronous event will be inserted between Pendding and termination. When the contract enters the termination state, the Promise handler will be scheduled to push the handler to the message queue.

Disadvantages:

  • Once executed, it cannot be cancelled midway
  • Without a callback function, errors thrown internally by a promise cannot be returned externally
  • When you’re pendding, you don’t know where you’re going
// Use timer
new Promise( (resolve, reject) = >{
    cancelFn( () = > {
        setTimeout( console.log, 0.'delay cancelled');
        resolve();
    });
});
// Encapsulate a promise, add a cancel method inside
reject('cancel promise');
Copy the code

To avoid processing a contract object synchronously based on the read contract state. Contracts deliberately encapsulate asynchronous behavior to isolate external synchronization code. The duality of synchronous and asynchronous execution:

try{
	throw new Error('foo');
}catch(e){
	console.log(e);//Error:foo successfully threw and caught the Error
}

try{
	Promise.reject(new Error('bar'));
}catch(e){
	console.log(e);
}
//Uncaught (in promise) Error: The bar throws an Error but does not catch it
Copy the code
  • If the callback function is not set, an error thrown inside a Promise is not thrown to the thread that executes the synchronized code, so a try/catch block cannot catch the error
  • Expiration rejection errors are handled through the browser asynchronous message queue, and once code starts executing in asynchronous mode, the only way to interact with it is to use asynchronous constructs — more specifically, methods of expiration.

Contract handling procedures

Is a bridge between external synchronous and asynchronous code, providing access to the return data of asynchronous operations, and to the success and failure results of processing contracts. Continuously evaluate the term, add code for state transitions… Contract chaining is a chain call: the methods then, Catch, and finally of each contract instance return a new contract object. Postrenewal Contract Indicates the period before waiting, serializing the asynchronous task.

Promise.prototype.then()

The main method to add a handler for about an instance, and the then() method receives the most two arguments: the onResolved handler and the onRejected handler. Both of these parameters are optional and will be executed when the contract enters the cash or reject state, respectively, if provided:


// Do not pass the specification for resolving listeners
p2.then(null.// Avoid creating redundancy in memory
		() = >onRejected('f'));

Copy the code

The cases in which promise calls Reject and resolve determine which of the then functions is called. Each time there is a return value but no explicit resolve, it is resolved and the first callback of the THEN handler is executed

var p = new Promise((resolve, reject) = > {resolve(1); }).then(() = >{console.log(1);return false; },() = >{console.log(2); reject(1); }).then((x) = >{console.log(3)},() = >{console.log(4)})
 
/ / 1 3
Copy the code

Thenable: Then ()

const then={
	then(callback){callback('baz');}
};
Copy the code

In ECMAScript exposed asynchronous constructs, any object has a then () method.

Promise.prototype.catch()

Used to add a handler to a contract that accepts only one parameter: the onRejected handler

  • Synchronization code fails, and errors bubble up the call stack until a catch block is hit
  • Asynchronous contract chain, error down the contract chain until a catch call is hit

After handling the error, you can recover from the error, returning a period of contract; If there are no errors it will be skipped

Promise.prototype.finally()

This handler is executed whenever the contract transitions to a resolved or rejected state. This can avoid duplicate code in onResolved and onRejected handlers, but no parameter is passed – you don’t know the date status is resolved/rejected, so it is mainly used to add clean code

  • Like catch and then, a term is returned, but is usually ignored.
  • The term value of finally generally depends on the term on which the finally callback was called, but if a finally callback throws an error, the returned term is rejected with this error (then, catch as well)

A static method for promise

Promise. Resolve, Promise. Reject

Any value can be converted to an immediately settled (but asynchronous) term, with the first parameter corresponding to the value of the resolved term.

const timing = new Promise( (resolve, reject) = >{
    setTimeout(resolve('succ'),1000);   
}).then(() = > console.log('p0 succ'),() = > console.log('p0 error'));
console.log('sync0');
Promise.reject(timing).then(() = > console.log('p1 succ'),() = > console.log('p1 error'));
console.log('sync1');
//[Log] sync0
//[Log] sync1
//[Log] p0 succ
//[Log] p1 error
Copy the code

? Only 2 is printed, and TEM is pending

const tem = new Promise( (resolve, reject) = >{
    return Promise.reject('erro').then(() = >console.log('1'), () = >console.log('2'));
    console.log('Will not be executed');
    resolve('succ');   
}).then(() = >console.log('3'), () = >console.log('4'));
Copy the code

? Uncaught (in promise) erro

const tem = new Promise( (resolve, reject) = >{
    return Promise.reject('erro');
    console.log('Will not be executed');
    resolve('succ');   
}).then(() = >console.log('3'), () = >console.log('4')).catch(() = >console.log('5'));
Copy the code

Promise. Resolve string promises into a task queue: If the promise. resolve method takes an object with the then method (also known as thenable), the method returns a term b, which will be rejected if a is rejected, as will resolve

var p = Promise.resolve('Hello');
 
p.then(function (s){
  console.log(s)
});
console.log("h")
//h Hello
Copy the code

Promise.resolve() is idempotent

p === Promise.resolve(p);
Copy the code

Promise.reject(error) is not idempotent

Promise.reject(p); // The contract object becomes the reason for rejection
Copy the code

Throw Error Error handling

Rejection contracts are similar to throw expressions in that throwing an error in the execution function or handler of a contract causes rejection, and the corresponding error object becomes the reason for rejection.

let p1=new Promise((resolve,reject) = >reject(Error('foo')));
let p2=new Promise((resolve,reject) = >{throw Error('foo'); }); p1.then(() = >{console.log('1true'); },() = >{console.log('1false')});
p2.then(() = >{console.log('2true'); },() = >{console.log('2false')});
Copy the code

Promise. All, Promise. Race

These two static methods take an iterable and return a new date

  • Promise.race returns a term that is a mirror image of the first term resolved or rejected in the incoming collection
  • Promise. All is highly correlated. If there is a failure, it will not continue to wait for the settlement of other promises. However, you can filter each Promise task in the Promise. Make sure that the promise is properly executed and goes to.then
getLatestJob(context){
      const result1=api.getJobJsonFromShield(context)
        .then(response= > {
          return response.json();
        });
      const result2=api.getJobJson(context)
        .then(response= > {
          return response.json();
        });

      Promise.all([result1, result2])
        .then(([shieldData, nbuData]) = >{
          context.commit('mergeList', {"shield":shieldData,"nbuData":nbuData})

        });
    }

Copy the code

Map: Overwrites incoming arguments, return === resolve

 
Promise.all([p1, p2, p3].map(p= > p.catch(e= > return 'Error returned value' )))
  .then(values= > {
    console.log(values);
  }).catch(err= > {
    console.log(err);
  })

Copy the code

The following apis can be simulated using both

none([]);
any([]);
first([]);
last([]);
Copy the code

Promise.allSettled

This is a big pity, which is a pity, which is a pity. This is a big pity, which is a pity, which is a rejected property.

Write a Promise by hand

Promises handle asynchronous issues and are essentially publish-subscribe. The first is a class that has an executor that accepts two callbacks, implementing the then method to get the result of either resolve or reject

A prototype

const PENDING = 'PENDING'; / / wait state
const FULFILLED = 'FULFILLED'; / / success
const REJECTED = 'REJECTED'; / / failure mode

class Promise1 {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
            
        const resolve = (value) = > {
                if (this.status === PENDING) {
                    this.status = FULFILLED;
                    this.value = value; }};const reject = (reason) = > {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason; }};try {// Throw an error
            executor(resolve, reject);
        } catch(error) { reject(error); }}// constructor
    
    then(onFulfilled, onRejected) {
        if (this.status === FULFILLED) {
            onFulfilled(this.value);
        }
        if (this.status === REJECTED) {
            onRejected(this.reason); }}}Copy the code

Support asynchronous

How do callbacks in then methods automatically trigger when resolve or Reject execute? Publish and subscribe model

const PENDING = 'PENDING'; / / wait state
const FULFILLED = 'FULFILLED'; / / success
const REJECTED = 'REJECTED'; / / failure mode

class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];// Implement asynchronous, publish-subscribe mode
    this.onRejectedCallbacks = [];
      
    const resolve = (value) = > {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach((fn) = >fn()); }};const reject = (reason) = > {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) = >fn()); }}try {// Throw an error
      executor(resolve, reject);
    } catch(error) { reject(error); }}// constructor
    
  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
    // Implement asynchronous
    if (this.status === PENDING) {
      this.onResolvedCallbacks.push(() = > {
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() = > {
        onRejected(this.reason); }); }}// then
}
Copy the code

Then returns a promise to support the chained call

X is the return value of resolve or reject

If x is a normal value, call the resolve handle, and if it is a promise, call the then method.

then(onFulfilled, onRejected) {
    // Each call to the THEN method must return a brand new promise
    let promise2 = new Promise((resolve, reject) = > {
    // x is the return value of the previous then success or failure, which determines whether promise2 succeeds or fails
        if (this.status == FULFILLED) {
            try {
                let x = onFulfilled(this.value);
                resolve(x);
            } catch(e) { reject(e); }}if (this.status == REJECTED) {
            try {
                let x = onRejected(this.reason);
                resolve(x);
            } catch(e) { reject(e); }}if (this.status == PENDING) {
            this.onResolvedCallbacks.push(() = > {
                try {
                let x = onFulfilled(this.value);
                    resolve(x);
                } catch(e) { reject(e); }});this.onRejectedCallbacks.push(() = > {
                try {
                    let x = onRejected(this.reason);
                    resolve(x);
                } catch(e) { reject(e); }}); }});return promise2;
}
Copy the code

Preventing circular calls

Fetch the promise2 variable in promise2, using a timer, and fetch it when the promise runs out

// Replace all processing of x.
setTimeout(() = > {
  try {
    let x = onFulfilled(this.value);
    resolvePromise(x, promise2, resolve, reject);
  } catch(e) { reject(e); }},0);
Copy the code
function resolvePromise(x, promise2, resolve, reject) {
    // If promise and x refer to the same object, reject promise with a TypeError as the reason
    if (x === promise2) {
        return reject(new TypeError('Circular reference'));
    }
    if ((typeof x === 'object'&& x ! = =null) | |typeof x == 'function') {
    //If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
        let called = false;
        try {
            let then = x.then; 
            if (typeof then == 'function') {
                then.call(x,
                    (y) = > {
                        // y may still be a promise, so the parsing process is done again
                        // I need to keep parsing the success value returned from the success promise until it is a normal value
                        if (called) return;
                        called = true;
                        resolvePromise(y, promise2, resolve, reject);
                    },
                    (r) = > {
                        if (called) return;
                        called = true; reject(r); }); }else{ resolve(x); }}catch (e) {
            if (called) return;
            called = true; reject(e); }}else{ resolve(x); }}Copy the code

An asynchronous function

Application of contract mode in functions, new specification in ES8

Declare asynchronous functions:

async function foo(){}
let foo=async() = > {};class P{
    / / class methods
    async p(){}}Copy the code

Async allows functions to be asynchronous. The return value of an asynchronous function is wrapped in promise.resolve () as a date object

await

  • Asynchronous functions are mainly for tasks that will not complete immediately and require an ability to pause and resume execution -await
  • Await can only be used in asynchronous functions.
  • An asynchronous function running to the await keyword will record the pause position, wait until the value to the right of the await is available, and then push a task to the message queue to resume execution of the asynchronous function.
async function awaitPro(){
    console.log('awaitPro1');
    console.log(await Promise.resolve(8));
    console.log('awaitPro2');
}

async function awaitIn(){
    console.log('awaitIn1');
    console.log(await 6);
    console.log('waitIn2');
}

console.log(1);
awaitPro();
console.log(2);
awaitIn();
console.log(3);
//1 awaitPro1 2 awaitIn1 3 6 awaitIn2 8 awaitPro2

Copy the code
  1. Print 1
  2. Call the asynchronous function awaitPro();
  3. Print (in awaitPro)
  4. (in awaitPro) await suspends execution and adds a task to the message queue that is scheduled to be executed after the execution
  5. The date is immediately settled and the task after await is added to the message queue
  6. AwaitPro exit
  7. Print 2
  8. Call awaitIn();
  9. Print waitIn1 (in awaitIn)
  10. (in awaitIn) await suspends execution and adds an immediately available task with value 6 to the message queue
  11. AwaitIn exit
  12. Print 3
  13. The top-level thread completes execution
  14. When the JavaScript runs, it takes a resolution-contract handler from the queue and feeds it the resolution-value 8
  15. At JavaScript runtime, add a task to the queue to resume executing the awaitPro function
  16. When JavaScript runs, retrieve the awaitIn task from the queue and value 6
  17. Print 6 in awaitIn
  18. Print waitIn2 (in awaitIn)
  19. WaitIn () returns
  20. When the asynchronous task completes, JavaScript runs, retrieve the task from the queue to execute awaitPro and value 8
  21. Print 8 (in awaitPro)
  22. Print waitPro2 (in awaitPro)

Resolve (8); resolve (6); resolve (8)

use

Realization of sleep.

Similar to Thread.sleep() in Java

async function sleep(delay) {
    return new Promise((res) = > setTimeout(res,delay));
}

async function fn() {
    const t0 = Date.now();
    await sleep(1500);
    console.log(Date.now()-t0);
}
fn();
Copy the code

Parallel and serial execution

I don’t really understand

Stack tracking and memory management

There is considerable overlap between the capabilities of the scheduled and asynchronous functions, but they behave very differently in memory

Iterate asynchronously for wait

Repetitive asynchronous events are used in asynchronous functions

const urls = [url1, url2, url3];
const promises = urls.map( url= > fetch(url) );
for( const p of promises) {
    response = await p;
    handle(response);
}
/ / is equivalent to
for await ( const response of promises) {
    handle(response);
}
Copy the code

Is this sequential asynchronous?

Asynchronous iterator

The browser

XMLHttpRequest is based on the Fetch API of Promise

fetch(URL)
    .then(response= > {
        return response.json();
    })
    .then(profile= > {
        displayUserProfile(profile);
    });
Copy the code

node

The Node server is asynchronous in nature, and has many apis that use callbacks and events, such as fs.readfile (), the default API for reading file contents.

other

Note that functional programming combined with Promise and the introduction of reactive programming can write nice callback code

Identify and determine if promise-like values are true promises :thenable

Instanceof libraries or frameworks may choose to implement their own promises instead of using the native ES6 implementation, and in fact, it’s quite possible that you’re using promises provided by libraries in earlier browsers that didn’t have promise implementations at all. The promise value might be received from another browser window (iframe, etc.), different from the current window /iframe.

extension

More value

Promises can only have one completion value, but may be needed in complex scenarios

Broken down into multiple promise signals: an array of promises

promise.all().then(
    function(msg){
        const [pro1, pro2] = msg;// Array decoupling});Copy the code

Cancel the promise

Many promise abstraction libraries improve the tools to cancel promises

Realization of others

I refer to this section

// Three states
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// Promise receives a function argument that executes immediately
function MyPromise(fn) {
  let _this = this;
  _this.currentState = PENDING;
  _this.value = undefined;
  // Used to hold callbacks in THEN, only when a promise
  // Cache only when the state is pending, and no more than one cache per instance
  _this.resolvedCallbacks = [];
  _this.rejectedCallbacks = [];

  _this.resolve = function (value) {
    if (value instanceof MyPromise) {
      // If value is a Promise, execute recursively
      return value.then(_this.resolve, _this.reject)
    }
    setTimeout(() = > { // Execute asynchronously to ensure the execution sequence
      if (_this.currentState === PENDING) {
        _this.currentState = RESOLVED;
        _this.value = value;
        _this.resolvedCallbacks.forEach(cb= >cb()); }})}; _this.reject =function (reason) {
    setTimeout(() = > { // Execute asynchronously to ensure the execution sequence
      if (_this.currentState === PENDING) {
        _this.currentState = REJECTED;
        _this.value = reason;
        _this.rejectedCallbacks.forEach(cb= >cb()); }})}// Used to solve the following problems
  // new Promise(() => throw Error('error))
  try {
    fn(_this.resolve, _this.reject);
  } catch (e) {
    _this.reject(e);
  }
}

MyPromise.prototype.then = function (onResolved, onRejected) {
  var self = this;
  // Specification 2.2.7, then must return a new promise
  var promise2;
  // Specification 2.2. OnResolved and onRejected are optional
  // Ignore the type if it is not a function, and pass through is also implemented
  // Promise.resolve(4).then().then((value) => console.log(value))
  onResolved = typeof onResolved === 'function' ? onResolved : v= > v;
  onRejected = typeof onRejected === 'function' ? onRejected : r= > throw r;

  if (self.currentState === RESOLVED) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      // Specification 2.2.4 ensures ondepressing, onRjected asynchronously
      // Use setTimeout
      setTimeout(function () {
        try {
          var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(reason) { reject(reason); }}); })); }if (self.currentState === REJECTED) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      setTimeout(function () {
        // Execute onRejected asynchronously
        try {
          var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(reason) { reject(reason); }}); })); }if (self.currentState === PENDING) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      self.resolvedCallbacks.push(function () {
        // We use a try/catch package to allow for possible errors
        try {
          var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(r) { reject(r); }}); self.rejectedCallbacks.push(function () {
        try {
          var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(r) { reject(r); }}); })); }};/ / specification 2.3
function resolutionProcedure(promise2, x, resolve, reject) {
  // Spec 2.3.1, x cannot be the same as promise2, avoid circular references
  if (promise2 === x) {
    return reject(new TypeError("Error"));
  }
  / / specification 2.3.2
  // If x is a Promise and the state is pending, continue to wait or execute
  if (x instanceof MyPromise) {
    if (x.currentState === PENDING) {
      x.then(function (value) {
        // This function is called again to confirm x resolve
        // What type is the argument? If it is a basic type, resolve again
        // Pass the value to the next then
        resolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    } else {
      x.then(resolve, reject);
    }
    return;
  }
  / / specification 2.3.3.3.3
  // reject or resolve if one executes, ignore the others
  let called = false;
  // Specification 2.3.3, determine whether x is an object or a function
  if(x ! = =null && (typeof x === "object" || typeof x === "function")) {
    Reject (reject); reject (reject)
    try {
      2.3.3.1 / / specification
      let then = x.then;
      // If then is a function, call x.teng
      if (typeof then === "function") {
        2.3.3.3 / / specification
        then.call(
          x,
          y= > {
            if (called) return;
            called = true;
            / / specification 2.3.3.3.1
            resolutionProcedure(promise2, y, resolve, reject);
          },
          e= > {
            if (called) return;
            called = true; reject(e); }); }else {
        2.3.3.4 / / specificationresolve(x); }}catch (e) {
      if (called) return;
      called = true; reject(e); }}else {
    // Specification 2.3.4, x is the basic typeresolve(x); }}Copy the code