JS asynchronous

The execution environment of JS language is “single thread”, that is, only one task can be completed at a time; If there are multiple tasks, they must be queued, the first task completed, the next task executed, and so on. The advantage of this mode is that the implementation is relatively simple, the execution environment is relatively simple; The disadvantage is that as long as one task takes a long time, subsequent tasks must wait in line, which will delay the execution of the entire program. A common browser non-response (suspended animation) is usually the result of a single piece of Javascript code running for so long (such as an infinite loop) that the entire page gets stuck in one place and no other task can be performed.

To solve this problem, the Javascript language divides the execution mode of the task into two modes: Synchronous and Asynchronous.

“Synchronous mode” is the mode of the previous section, the latter task wait for the end of the previous task, and then execute, the execution order of the program is consistent with the order of tasks, synchronous; Asynchronous mode “is completely different, each task has one or more of the callback function (the callback), before the end of a task, not to perform a task, after but the callback function, after a task is before the end of a task before execution, so the program execution order the order is not consistent with the task, asynchronous.

“Asynchronous mode” is very important. On the browser side, long operations should be performed asynchronously to prevent the browser from becoming unresponsive. The best example of this is Ajax operations. On the server side, “asynchronous mode” is even the only mode, because the execution environment is single-threaded, and if you allow all HTTP requests to be executed synchronously, the server performance deteriorates dramatically and quickly becomes unresponsive.

Common asynchronous programming patterns

  1. The callback functions are f1 and f2, and F2 waits for the result of f1 execution, namely f1(F2).
  2. Event-driven f1.on(‘done’, f2); (JQ, f1 complete, trigger(“done”) f2)
  3. Publish and subscribe
  4. Promise object implementation

Promise object

I believe everyone in the process of learning ES6 are more or less learned ruan teacher ES6 tutorial, so here is a simple example to tell about the characteristics and use of Promise objects

Basic method of use

ES6 provides a Promise constructor. We create an instance of a Promise. The Promise constructor takes a function as an argument. Resolve changes the state of the Promise from unsuccessful to successful, passing the result of the asynchronous operation as a parameter; Similarly, reject changes the state from reject to fail, is called when an asynchronous operation fails, and any errors reported by the asynchronous operation are passed as arguments. Once the instance is created, you can use the THEN method to specify successful or failed callback functions, which is more elegant and readable than the layer upon layer writing of f1(F2 (F3))

let promise = new Promise((resolve, reject)=>{
    reject("Refused.");
});
promise.then((data)=>{
    console.log('success'+ data); }, (error)=>{ console.log(error) }); Execution Result:"Refused."

Copy the code

The characteristics of the Promise

  • The initial state of the object is Pending, and the result states are resolve and Reject. Only the result of the asynchronous operation determines this state
  • The status can only be changed from Pending to one of the other two. The change cannot be reversed or changed again, namely, Pending -> Resolved or Pending -> REJECT
let promise = new Promise((resolve, reject)=>{
    reject("Refused.");
    resolve("Passed again.");
});
promise.then((data)=>{
    console.log('success'+ data); }, (error)=>{ console.log(error) }); Execution Result:"Refused."

Copy the code

The code above will no longer execute the resolve method

Rules for the THEN method

  • thenThe next input to the method requires the last output
  • If another promise is returned after a promise is executed, the execution result of the promise is passed to the next timethenIn the
  • ifthenReturns a normal value instead of a Promise object, which will be the result of the success of the next THEN
  • If the currentthenIf you fail, you go nextthenThe failure of the
  • If undefined is returned, success or failure will be followed by the next success
  • Catch is when an error is not handled
  • thenIf the method is not written to, the value is passed through to the next onethenIn the

Use the node FS module to read the file flow to test that we create a method to read the file, defining in the Promise that if the read succeeds, the file’s contents will be displayed, otherwise an error will be reported

let fs = require('fs');

function read(file, encoding) {
    return new Promise((resolve, reject)=>{
        fs.readFile(filePath, encodeing, (err, data)=> {
            if(err) reject(err); resolve(data); }); })}Copy the code

Because we want to see multiple coherent callback, we specially set up 3 TXT files, where the contents of file 1 is the file name of file 2, the content of file 2 is the file name of file 3, and the final content of file 3 is displayed

The execution code is as follows:

read('1.promise/readme.txt'.'utf8').then((data)=>{
    console.log(data)
});
Copy the code

To print readme2.txt for reading a file, we modified this code to add multiple callbacks, returning the currently returned Promise object in all then before the last one

read('readme.txt'.'utf8').then((data)=>{
    return read(data, 'utf8');
}).then((data)=>{
    return read(data, 'utf8') }).then((data)=>{ console.log(data); }); The final output is the contents of readme3.txtCopy the code

For the next then step, we process and return the contents of readme3.txt

read('readme.txt'.'utf8').then((data)=>{
    return read(data, 'utf8');
}).then((data)=>{
    return read(data, 'utf8')
}).then(data=>{
    return data.split(' ').reverse().join(); // This step returns a normal value, the normal value is the nextthen}). Then (null,data=>{// throw new Error();'wrong'}). Then (data=>{console.log(data) // Will print the normal value of the previous step});Copy the code

Here we pass a normal value to the next THEN after processing the content, but since the next THEN did not handle the successful method (NULL)

Finally, we look at error handling. After processing the result of readme3.txt, we pass the value to the next THEN and make it open as a file name. However, the nonexistent file is no longer found, and the error result is printed in the last step

read('readme.txt'.'utf8').then((data)=>{
    return read(data, 'utf8');
}).then((data)=>{
    return read(data, 'utf8')
}).then(data=>{
    return data.split(' ').reverse().join();
}).then(null,data=>{
    throw new Error('wrong')
}).then(data=>{
    return read(data, 'utf8')
}).then(null,(err)=>{
    console.log(err)
});
Copy the code

Results:

Promises A+ (Promises Aplus)

Promises Aplus specification that specifies the principle of Promise, source code, etc., through the specification, we can achieve a Promise class library based on PromiseA + specification, here we show the realization of the source

/ * * * Promise to follow the Promise/A + * official station: https://promisesaplus.com/ * Promise/A + specification, * https://malcolmyu.github.io/2015/06/12/Promises-A-Plus/#note-4*/ // promise three states const PENDING ="pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function Promise(excutor) {
    letself = this; // Cache the current Promise instance object self.status = PENDING; Self. value = undefined; // this is a big pity. Self. reason = undefined; / / rejected state refused to reason the self onFulfilledCallbacks = []; // Store the ondepressing function self.onRejectedCallbacks = []; // Stores the onRejected function corresponding to the Rejected statefunctionResolve (value) {// value Specifies the final value to be received in the successful stateif(value instanceof Promise) {
            returnvalue.then(resolve, reject); } // why resolvesetTimeout? OnFulfilled and onRejected are only allowed to run when the execution Context stack contains only the platform code, which refers to the engine, environment and promise implementation code. In practice, make sure that the onFulfilled and onRejected methods are implemented asynchronously and should be fulfilled in thethenThe method is executed in a new stack after the event loop in which it was called.setTimeout(() => {// Call the resolve callback corresponding to the ondepressing functionif(self.status === PENDING) {// This is a big pity; self.value = value; self.onFulfilledCallbacks.forEach(cb => cb(self.value)); }}); }functionReject (reason) {// Reason indicates the reason for receiving in the failed statesetTimeout(() => {// Calls the reject callback corresponding to the onRejected functionif(self.status === PENDING) {// Only the pedning status => Reject status (resolve reject) self.status = rejected; self.reason = reason; self.onRejectedCallbacks.forEach(cb => cb(self.reason)); }}); } // new Promise((resolve, reject) => {// throw new Error()'error in excutor') // }) try { excutor(resolve, reject); } catch (e) { reject(e); }}Copy the code

In this part of the code we judge resolve and reject, and then we construct the then method

/** * (this is a big pity /rejected state) * @param {function} @param {function} onRejected Rejected * @return {function} promise2 returns a new promise object */ promise.prototype. then =function(onFulfilled, onRejected) {// This is a big pity, onRejected) {// This is a big pity, onRejected)let self = this;
    letpromise2; // Each call is requiredthenPromise2 = new promise ((resolve, reject) => {// success stateif (self.status === 'resolved') {
            setTimeout(()=>{try {// An exception may occur when a successful callback is executed, so use this exception as the result of a promise2 errorletx = onFulfilled(self.value); / / after the execution of the current success callback returns results may be a promise resolvePromise (promise2, x, resolve, reject); } catch (e) { reject(e); }},0)} // Failed stateif (self.status === 'rejected') {
            setTimeout(()=>{
                try {
                    letx = onRejected(self.reason); resolvePromise(promise2,x,resolve,reject); } catch (e) { reject(e); }}}, 0)if (self.status === 'pending') {/ / wait state, when a call to resolve/reject, collect onFullfilled/onReject temporary to the collection of the self. OnResolvedCallbacks. Push (() = > {setTimeout(()=>{
                    try {
                        letx = onFulfilled(self.value); resolvePromise(promise2,x,resolve,reject); } catch (e) { reject(e); }}}, 0)); self.onRejectedCallbacks.push(() => {setTimeout(()=>{
                    try {
                        letx = onRejected(self.reason); resolvePromise(promise2,x,resolve,reject); } catch (e) { reject(e); }}}, 0)); }});returnPromise2} // Where the specification calls for the callback to be addedsetThe Timeout processingCopy the code

As you can see, both resolve and Reject have a new promise method, resolvePromise, that encapsulates it to handle different cases

@param {promise} promise2 Promise1. Then new promise object * @param {[type]} x promise1 * @param {[type} @param {[type]} reject Reject method */function resolvePromise(promise2,x,resolve,reject){
    if(promise2 === x){// If x returned from onFullfilled is promise2, this will cause a circular reference errorreturn reject(new TypeError('Chaining cycle'));
    }
    letcalled; Avoid using the // x type multiple times to determine if it is an object or functionif(x! ==null && (typeof x==='object' || typeof x === 'function'){// Check whether thenable object try{let then = x.then; 
            if(typeof then= = ='function'){
                then.call(x,y=>{ 
                    if(called) return; 
                    called = true;
                    resolvePromise(promise2,y,resolve,reject);
                },err=>{ 
                    if(called) return;
                    called = true;
                    reject(err);
                });
            }elseResolve (x); } }catch(e){if(called) return;
            called = true; reject(e); }}else{ resolve(x); }}Copy the code

The above basically implement the basic method of Promise, according to the use of Promise, supplement some methods on the class

// Catch the exception promise = that onFulfilled/onRejected throws when used in the promise method chainfunction(reason){
    return new Promise((resolve,reject)=>{
        reject(reason);
    })
}
Promise.resolve = function(value){
    return new Promise((resolve,reject)=>{
        resolve(value);
    })
}
Promise.prototype.catch = function(onRejected){// Write successfullyreturnthis.then(null,onRejected); }; /** * Promise. All Promise is processed in parallel * Parameters: array of Promise objects as parameters * return value: Return a Promise instance * Resolve is resolved when all the Promise objects in this array are in the resolve state. */ Promise.all =function(promises){
    return new Promise((resolve,reject)=>{
        let arr = [];
        let i = 0;
        function processData(index,data){
            arr[index] = data;
            if(++i == promises.length){ resolve(arr); }}for(leti = 0; i<promises.length; I ++){promises[I]. Then (data=>{// data=>{processData(I,data); },reject); }})} /** * promise.race * argument: accepts an array of Promise objects as arguments * returns a value: Return a Promise instance * As long as one of the Promise objects enters the FulFilled or Rejected state, the processing will continue (depending on which is faster) */ promise.race =function(promises){
    return new Promise((resolve,reject)=>{
        for(let i = 0;i<promises.length;i++){
            promises[i].then(resolve,reject);
        }
    })
}

Copy the code

Finally, we export the method

module.exports = Promise;
Copy the code

Now that you have a PromiseA+ written source code base, you can test using it instead of promises to test for logic errors, etc., or you can use it

NPM install Promises -aplus-tests -g promises-aplus-testCopy the code

Plugin to test the source code for compliance with the PromiseA+ specification

Hope this article can help you, above