The meaning and meaning of Promise

Promise is a component that abstracts asynchronous processing objects and performs various operations on them. In fact, Promise is an object used to deliver messages of asynchronous operations. It is not a language-specific attribute, but ES6 has written it into the language standard, unified its usage, and provided Promise objects primitively. Promise objects have the following two characteristics:

2. Once the state changes, it will never change again. This result can be obtained at any time

Promise also has the following disadvantages:

1. Cannot cancel a Promise. Once a Promise is created, it will be executed immediately and cannot be cancelled midway. 2. If you don’t set a callback function, errors thrown inside a Promise won’t be reflected outside. 3. When in a Pending state, there is no way to know what stage of progress is currently in (just started or nearly completed).

For a detailed introduction to and use of Promises, see the JavaScript Promise mini-book

ES6 adds promises because asynchronous operations in JS are usually handled using callback functions and events. With Promise objects, asynchronous operations can be expressed as a flow of synchronous operations, eliminating layers of nested callback functions. In addition, Promise objects provide a unified interface that makes it easier to control asynchronous operations. Taking node.js as an example, asynchronous javascripts used to make use of callback functions like this:

var fs = require('fs');
fs.readFile('demo.txt'.'utf8'.function (err, data) {
          if (err) throw err;
         console.log(data);
});
Copy the code

Instead, use promises like this:

var fs = require('fs');
function readFile(fileName) {
    return new Promise(function(resolve, reject) {
        fs.readFile(fileName, function (err, data) {
            if (err) {
                reject(err);
            } else{ resolve(data); }}); }); }readFile(('demo.txt').then(
    function(data) {
        console.log(data);
    }, 
    function(err) { throw err; });Copy the code

If we have multiple delay tasks to handle, we will use setTimeout in JS. In the past, js is often written like this:

var taskFun = function() {   
    setTimeout(function() {
               // do timeoutTask1
              console.log("do timeoutTask1");
        setTimeout(function() {
                   // do timeoutTask2
                  console.log("do timeoutTask2");
            setTimeout(function() {
                      // dotimeoutTask3
                     console.log("do timeoutTask3");
            }, 3000);
        }, 1000); 
    }, 2000);
}
 taskFun();
Copy the code

If the business logic gets a little more complicated, it goes into what’s called callback hell, so if you use Promise, you can write it like this:

new Promise(function(resolve, reject) {
    console.log("start timeoutTask1");
    setTimeout(resolve, 3000);
}).then(function() {
    // do timeoutTask1
    console.log("do timeoutTask1");
    return new Promise(function(resolve, reject) {
        console.log("start timeoutTask2");
        setTimeout(resolve, 1000);
    });
}).then(function() {
    // do timeoutTask1
    console.log("do timeoutTask2");
    return new Promise(function(resolve, reject) {
        console.log("start timeoutTask3");
        setTimeout(resolve, 2000);
    });
}).then(function() {
    // do timeoutTask1
    console.log("do timeoutTask3");
});
Copy the code

We could also use Promise to break down each task into separate functions to make the code look more elegant and intuitive:

function timeoutTask1() {
    return new Promise(function(resolve, reject) {
        console.log("start timeoutTask1");
        setTimeout(resolve, 3000);
    });
}

function timeoutTask2() {
    return new Promise(function(resolve, reject) {
        console.log("start timeoutTask2");
        setTimeout(resolve, 1000);
    });
}

function timeoutTask3() {
    return new Promise(function(resolve, reject) {
        console.log("start timeoutTask3");
        setTimeout(resolve, 2000);
    });
}

timeoutTask1()
    .then(function() {
        // do timeoutTask1
        console.log("do timeoutTask1");
    })
    .then(timeoutTask2)
    .then(function() {
        // do timeoutTask2
        console.log("do timeoutTask2");
    })
    .then(timeoutTask3)
    .then(function() {
        // do timeoutTask2
        console.log("do timeoutTask3");
    });
Copy the code

The order of execution is:

Implement A Promise that follows the Promise/A+ specification by ES6 itself

Promise/A+ is A mainstream specification of promises. Browsers, Node and JS libraries use this specification to implement the corresponding functions. Implementing A Promise by this specification can also be called implementing A Promise/A+. Please refer to the detailsPromise/A + specification

1. The class and constructor construct Promise’s argument is a function task that passes internally defined resolve and reject methods as parameters to call the task. The resolve method is called on success and the callback registered in THEN is executed, while the Reject method is called on failure.

class Promise {
    constructor(task) {
        letself = this; // Cache this self.status ='pending'; // Default status is pending self.value = undefined; Self.onresolvedcallbacks = []; self.onresolvedCallbacks = []; Self.onrejectedcallbacks = []; self.onrejectedCallbacks = []; // Call resolve to change the Promise state to a success statefunction resolve(value) {
            if (value instanceof Promise) {
                return value.then(resolve, reject)
            }
            setTimeout() => {// Asynchrony (asynchrony) => {// Asynchrony (asynchrony) => {// Asynchrony (asynchrony) => {// Asynchrony (asynchrony) => { The two cannot convert into each otherif (self.status == 'pending') {
                    self.value = value;
                    self.status = 'resolved'; self.onResolvedCallbacks.forEach(item => item(self.value)); }}); } // Call reject to change the current PROMISE state to a failed statefunction reject(value) {
            setTimeout(() => {
                if (self.status == 'pending') {
                    self.value = value;
                    self.status = 'rejected'; self.onRejectedCallbacks.forEach(item => item(value)); }}); } // Execute the incoming task immediately try {task(resolve, reject); } catch (e) { reject(e); }}}Copy the code

Code ideas and key points:

  • Self = this, don’t worry about this pointing to the sudden change problem.
  • Each Promise has three mutually exclusive states: Pending, depressing, and Rejected.
  • There are only two possibilities for the state of the Promise object to change from pending to depressing and from pending to Rejected. As long as those two things happen, the state is frozen, it’s not going to change, it’s going to stay the same. If you add a callback to the Promise object, you’ll get the same result immediately, even if the change has already occurred. This is completely different from an event, where if you miss it and listen again, you don’t get results.
  • Create a Promise object and call its task, passing in resolve and Reject. When the asynchronous operation of the Task succeeds, resolve will be called. That is, execute the callback in the Promise. onResolvedCallbacks array, and do the same if it fails.
  • The resolve and reject methods accept a value, the result of an asynchronous operation, to facilitate value transfer.

2.Promise.prototype. Then chain support

This is a big pity. /** * this is a big pitythen(onFulfilled, onRejected) {
        letself = this; Ondepressing = isFunction(ondepressing)? onFulfilled : value => value; onRejected = isFunction(onRejected) ? onRejected : value => { throw value };let promise2;
        if (self.status == 'resolved') {
            promise2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        letx = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); }}); }); }if (self.status == 'rejected') {
            promise2 = new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        letx = onRejected(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); }}); }); }if (self.status == 'pending') {
            promise2 = new Promise((resolve, reject) => {
                self.onResolvedCallbacks.push(value => {
                    try {
                        letx = onFulfilled(value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); }}); self.onRejectedCallbacks.push(value => { try {letx = onRejected(value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); }}); }); }return promise2;
    }
Copy the code

Code ideas and key points:

  • Call then method, the success callback into a promise. OnResolvedCallbacks array; Failure callback into a promise. OnRejectedCallbacks array
  • Returns an instance of Promise promise2 for the convenience of chained invocation
  • The return Promise2 in the then method implements chained calls
  • If the incoming is a function of an asynchronous operation does not contain, and resolve will precede then execution, namely promise. OnResolvedCallbacks is an empty array, in order to solve this problem, in adding setTimeout resolve function, Place the logic to execute the callback in resolve at the end of the JS task queue; The same thing with reject.

3. Static method promise.resolve

 static resolve(value) {
        return new Promise((resolve, reject) => {
            if(typeof value ! == null && typeof value ==='object' && isFunction(value.then)) {
                value.then();
            } else{ resolve(value); }})}Copy the code

The static promise.resolve (value) method can be thought of as a shortcut to the new Promise() method.

Such as Promise. Resolve (666); Think of it as syntactic sugar for the following code.

new Promise(function(resolve){
    resolve(666);
});
Copy the code

4. Static method Promise.reject

 static reject(err) {
        returnnew Promise((resolve, reject) => { reject(err); })}Copy the code

Promise.reject(err) is a static method similar to promise.resolve (value) that is a shortcut to the New Promise() method.

For example, promise.reject (new Error(” Error “)) is the syntactic sugar form of the code below.

new Promise(function(resolve,reject){
    reject(new Error("Something went wrong."));
});
Copy the code

4. Static method promise.all

/** * static all(promises) {/** * static all(promises) {/** * static all(promises) {/** * static all(promises) {/** * static all(promises) {return new Promise((resolve, reject) => {
            letresult = []; // The final result of the all methodletcount = 0; // The number of completionsfor (let i = 0; i < promises.length; i++) {
                promises[i].then(data => {
                    result[i] = data;
                    if(++count == promises.length) { resolve(result); } }, err => { reject(err); }); }}); }Copy the code

Promise.all takes an array of Promise objects as arguments, and calls the.then method when all the Promise objects in the array are resolved or reject. Resolve returns an array of all resolve execution results if all are resolve. If any state is not resolve, return the failure result of that state.

5. Static method promise.race

/** * static race(promises) {/ / Promises (promises) {/ / Promises (promises) {/ / Promises (promises) {/ / Promises (promises) {/ / Promises (promises) {/ / Promises (promises) {/ / Promises (promises) {/ / Promises (promises) {return new Promise((resolve, reject) => {
            for (leti = 0; i < promises.length; i++) { promises[i].then(data => { resolve(data); },err => { reject(err); }); }}); }Copy the code

Promise.race is similar to promise.all in that it also receives an array. Race means race, and as the name suggests, if it’s a race, there’s a unique winner, So the biggest difference between all and Promise is that it returns any state in the array that changes (resolve or reject), so it outputs only the state that was executed first. Instead of all returning an array when all is in the resolve state. A simple modification of the promise.all method implements race.

3. To summarize

The above is an introduction to some of the main methods, but some are not completely introduced, you can refer to the source code, the source file contains a test folder and the es5 version of the source, more detailed explanation will be provided later. You can also test the implementation promise specification by installing a plug-in.

npm(cnpm) i -g promises-aplus-tests
promises-aplus-tests es6Promise.js
Copy the code