Promise object

  • Promise is a solution to asynchronous programming that makes more sense and is more powerful than traditional solutions — callback functions and events.

  • A Promise is simply a container that holds the result of an event (usually an asynchronous operation) that will end in the future. Syntactically, a Promise is an object from which to get messages for asynchronous operations. Promise provides a uniform API, and all kinds of asynchronous operations can be handled in the same way.

  • Promise objects have two characteristics.

    (1) The state of the object is not affected by the outside world. The Promise object represents an asynchronous operation with three states: Pending, fulfilled and Rejected. Only the result of an asynchronous operation can determine the current state, and no other operation can change the state. That’s where the name “Promise” comes from. Its English name means “Promise,” indicating that nothing else can change it.

    (2) Once the state changes, it will never change again, and this result can be obtained at any time. 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 these two things are happening the state is fixed, it’s not going to change, it’s going to stay the same and that’s called resolved.

Examples of how to use the callback function

  • Motion of the cube
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, < span style>. Box {height: 100px; width: 100px; position: absolute; left: 0; top: 0; background-color: aqua; } < / style > < div class = "box" > < / div > < body > < script > / / the window. The getComputedStyleWindow. GetComputedStyle () method returns an object, This object reports the values of all CSS properties of the element after the active stylesheet is applied and any basic calculations that those values may contain are parsed. let ele = document.querySelector(".box") let el = window.getComputedStyle(ele,null)["height"] function Move (ele,arg,target,cb) {let start = parseInt(window.getcomputedStyle (ele,null)[arg] Math. Abs (target-start) // let speed = dis * 4; Function fn() {let now = parseInt(window.getcomputedStyle (ele,null)[arg]) if(now == target) {cb&&cb(" 新 完 全 ") }else { ele.style[arg] = now + speed + 'px' setTimeout(fn, 50) } } fn(); } // move(ele,"left",200,function(res){// move right over console.log(res); Move (ele,"top",200,function(res){// move down console.log(res); Move (ele,"left",0,function(res){// move console.log(res); Move (ele, "top", 0) / / upward movement})})}) < / script > < / body > < / HTML >Copy the code

2. Basic usage

ES6 specifies that a Promise object is a constructor that generates a Promise instance.

The following code creates an instance of Promise.

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* Asynchronous operation succeeded */){
    resolve(value);
  } else{ reject(error); }});Copy the code

The Promise constructor takes a function as an argument, resolve and reject. They are two functions that are provided by the JavaScript engine and do not need to be deployed themselves.

The resolve function changes the state of the Promise object from “unfinished” to “successful.” It will be called when the asynchronous operation succeeds and will pass the result of the asynchronous operation as an argument. The Reject function changes the state of the Promise object from “unfinished” to “failed” (i.e., from Pending to Rejected). It is called when the asynchronous operation fails and passes the error reported by the asynchronous operation as a parameter.

3, Promise. Then ()

Promise instances have THEN methods, that is, then methods defined on the prototype object Promise.Prototype. It adds a callback function to the Promise instance when the state changes. As mentioned earlier, the first argument to the THEN method is the resolved state callback and the second argument is the Rejected state callback, both of which are optional.

After the Promise instance is generated, you can use the THEN method to specify the resolved and Rejected state callback functions, respectively.

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});
Copy the code
// The Promise object is pending, flufilled(succeeded), rejected (failed).
let p = new Promise(function(resolve,reject) {
    resolve("success");
    reject("err");
})
console.log(p);
The then method takes as arguments two callback functions, resolve on success and Reject on failure
p.then(function(res){
    console.log("Successful callback",res)
},function(err){
    console.log("Failed callback",err)
})
Copy the code

Promises are implemented as soon as they are created.

let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function() {
  console.log('resolved.');
});

console.log('Hi! ');

// Promise
// Hi!
// resolved
Copy the code

In the code above, the Promise is executed immediately after it is created, so the Promise is printed first. Then, the callback specified by the then method will not execute until all synchronization tasks in the current script have finished, so resolved will output.

  • Here is an example of loading images asynchronously.
function onloadImg1() {
    return new Promise(function (resolve, reject) {
        let img = new Image();
        img.onload = function () {
            resolve("Load complete");
        }
        img.onerror = function () {
            reject("Load failed");
        }
        img.src='https://gimg2.baidu.com/image_search/src=http%3A%2F%2F1812.img.pp.sohu.com.cn%2Fimages%2Fblog%2F2009%2F11%2F18%2F18%2F8 %2F125b6560a6ag214.jpg'
    })
}
onloadImg1().then(res= > {
    console.log(res);
}, err= > {
    console.log(err);
})
Copy the code
  • Block motion Promise transformation
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, < span style>. Box {height: 100px; width: 100px; position: absolute; left: 0; top: 0; background-color: aqua; } < / style > < div class = "box" > < / div > < body > < script > / / the window. The getComputedStyleWindow. GetComputedStyle () method returns an object, This object reports the values of all CSS properties of the element after the active stylesheet is applied and any basic calculations that those values may contain are parsed. let ele = document.querySelector(".box") let el = window.getComputedStyle(ele, null)["height"] function move(ele, arg, target) { return new Promise((resolve, reject) => { let start = parseInt(window.getComputedStyle(ele, null)[arg]) let dis = (target - start) / Math.abs(target - start) let speed = dis * 5; function fn() { let now = parseInt(window.getComputedStyle(ele, Null)[arg]) if (now == target) {// cb && cb(" finish ")} else {ele. Style [arg] = now + speed + 'px' setTimeout(fn, 50) } } fn(); }) } move(ele, "left", 200).then(res => { console.log(res); return move(ele, "top", 200) }).then(res => { return move(ele, "left", 0) console.log(res); }).then(res => { return move(ele, "top", 0) console.log(res); }).then(res => { console.log(res); }) </script> </body> </html>Copy the code

4, Promise. Finally ()

The finally() method is used to specify actions that will be performed regardless of the final state of the Promise object. This method was introduced as a standard in ES2018.

promise
.then(result= >{...}). The catch (error= >{...}). Finally,() = > {···});
Copy the code

In the code above, regardless of the last state of the promise, the callback specified by the finally method is executed after the callback specified by then or catch.

Write promises by hand

5-1 Statement of the Promise
  • The Promise object is a constructor that generates a Promise instance.

  • Since new Promise((resolve, reject)=>{}), an argument (function) executor is passed in and executed.

  • Executor takes two arguments, resolve and reject.

  • Resolve and reject are executable

class Promise {
    / / the constructor
    constructor(executor) {
        // Callback on success
        let resolve = () = > {}
        // Callback on failure
        let rejtect = () = > {}
        // Execute immediatelyexecutor(resolve, reject); }}Copy the code
5-2 Resolve the Promise state
  • There are three states: pending, fulfilled and rejected. This is a pity.

  • On success, it cannot go to another state and must have an immutable value.

  • When a failure occurs, it cannot be changed to another state, and there must be an immutable reason.

  • New Promise((resolve, reject)=>{resolve(value)}) resolve is successful, and the parameter value is accepted. The state changes to a pity, which cannot be changed again.

  • New Promise((resolve, reject)=>{reject(reason)}) Reject indicates the reject parameter. Reject indicates the reject parameter.

  • If the executor function fails, execute reject().

    class Promise {
        / / the constructor
        constructor(executor) {
            // Initial state
            this.state = 'pending'
            // Success value
            this.value = undefined;
            // Failure cause
            this.reason = undefined
            // Callback on success
            let resolve = (value) = > {
                if (this.state = 'pending') {
                    // State changes to a successful state after the resolve call
                    this.state = 'flufilled'
                    // Store the value of value
                    this.value = value
                }
            }
            // Callback on failure
            let rejtect = (reason) = > {
                if (this.state = 'pending') {
                    // State changes to a failed state after the resolve call
                    this.state = 'rejected'
                    // Store the reason for reason
                    this.reason = reason
                }
            };
            try {
                // Execute immediately
                executor(resolve, reject);
            } catch (err) {
                rejtect(err)
            }
        }
    }
    Copy the code
5-3 then method
  • thenThe first argument to the method isonFulfilledThe second argument to the state callback function isonRejectedState callback functions, which are optional. Success has the value of success, failure has the cause of failure
  • When the state is depressing, ondepressing will be executed and this.value will be passed. If the state is rejected, execute onRejected and pass this.reason
  • OnFulfilled,onRejected If they are functions, they must be called after the fulfilled,onRejected, respectively, and value or reason must be their first parameter in turn
class Promise {
    / / the constructor
    constructor(executor) {
        // Initial state
        this.state = 'pending'
        // Success value
        this.value = undefined;
        // Failure cause
        this.reason = undefined
        // Callback on success
        let resolve = (value) = > {
            if (this.state = 'pending') {
                // State changes to a successful state after the resolve call
                this.state = 'flufilled'
                // Store the value of value
                this.value = value
            }
        }
        // Callback on failure
        let rejtect = (reason) = > {
            if (this.state = 'pending') {
                // State changes to a failed state after the resolve call
                this.state = 'rejected'
                // Store the reason for reason
                this.reason = reason
            }
        };
        try {
            // Execute immediately
            executor(resolve, reject);
        } catch (err) {
            rejtect(err)
        }
    }
    then(onFlufilled, onRejected) {
        if (this.state == 'fulfilled') {
            // Perform ondepressing and pass in the successful value
            onFlufilled(this.value)
        }
        if (this.state == 'rejected') {
            // This is a big pity. Execute onRejected and pass in the failed value
            onRejected(this.reason)
        }
    }
}
Copy the code
5-4 Address asynchronous implementations

Because a promise can have more than one THEN, it exists in the same array.

// Multiple THEN cases
let p = new Promise(a); p.then(); p.then();Copy the code
  • ForEach calls them on success or failure
class Promise {
    / / the constructor
    constructor(executor) {
        // Initial state
        this.state = 'pending'
        // Success value
        this.value = undefined;
        // Failure cause
        this.reason = undefined;
        // Store the successful array
        this.onResolvedArr = [];
        // Store the failed array
        this.onRejectedArr = [];
        // Callback on success
        let resolve = (value) = > {
            if (this.state = 'pending') {
                // State changes to a successful state after the resolve call
                this.state = 'flufilled'
                // Store the value of value
                this.value = value;
                // Once reslove is executed, the array is successfully called
                this.onResolvedArr.forEach(fn= > fn())
            }
        }
        // Callback on failure
        let rejtect = (reason) = > {
            if (this.state = 'pending') {
                // State changes to a failed state after the resolve call
                this.state = 'rejected'
                // Store the reason for reason
                this.reason = reason;
                // Once reject executes, the array that failed is called
                this.onRejectedArr.forEach(fn= > fn())
            }
        };
        try {
            // Execute immediately
            executor(resolve, reject);
        } catch (err) {
            rejtect(err)
        }
    }
    then(onFlufilled, onRejected) {
        if (this.state == 'fulfilled') {
            // Perform ondepressing and pass in the successful value
            onFlufilled(this.value)
        }
        if (this.state == 'rejected') {
            // This is a big pity. Execute onRejected and pass in the failed value
            onRejected(this.reason)
        }
        // State is pending
        if (this.state == 'pending') {
            this.onResolvedArr.push(() = > {
                onFlufilled(this.value)
            })
            this.onRejectedArr.push(() = > {
                onRejected(this.reason)
            })
        }
    }
}
Copy the code
5-5 solve the chain call

We use it all the timenew Promise().then().then()This is the chain call to solve callback hell

1. To achieve the chain, we return a promise in the first THEN by default. Promise2 = new promise (resolve, reject)=>{})

  • Pass the value returned by this promise2 into the next THEN
  • If a normal value is returned, the normal value is passed to the next THEN

2. If we return a parameter in the first then. This new promise that comes out of return is onFulfilled() or onRejected()

The value of onFulfilled() or onRejected(), which is the value returned by the first THEN, is called X. The function to judge X is called resolvePromise

  • First of all, let’s see if x is a promise.
  • If it is a promise, take its outcome as the outcome of the new promise2 success
  • If the value is normal, it is used as the result of the success of promise2
  • So compare X to promise2
  • ResolvePromise parameters are promisE2 (the promise returned by default), x (ourselves)returnObject, resolve, reject
  • Resolve and Reject are promise2
class Promise {
    / / the constructor
    constructor(executor) {
        // Initial state
        this.state = 'pending'
        // Success value
        this.value = undefined;
        // Failure cause
        this.reason = undefined;
        // Store the successful array
        this.onResolvedArr = [];
        // Store the failed array
        this.onRejectedArr = [];
        // Callback on success
        let resolve = (value) = > {
            if (this.state = 'pending') {
                // State changes to a successful state after the resolve call
                this.state = 'flufilled'
                // Store the value of value
                this.value = value;
                // Once reslove is executed, the array is successfully called
                this.onResolvedArr.forEach(fn= > fn())
            }
        }
        // Callback on failure
        let rejtect = (reason) = > {
            if (this.state = 'pending') {
                // State changes to a failed state after the resolve call
                this.state = 'rejected'
                // Store the reason for reason
                this.reason = reason;
                // Once reject executes, the array that failed is called
                this.onRejectedArr.forEach(fn= > fn())
            }
        };
        try {
            // Execute immediately
            executor(resolve, reject);
        } catch (err) {
            rejtect(err)
        }
    }
    then(onFlufilled, onRejected) {
        let promise2 = new Promise((resolve, reject) = > {
            if (this.state == 'fulfilled') {
                // Perform ondepressing and pass in the successful value
                onFlufilled(this.value);
                // the resolvePromise function handles the relationship between its return promise and the default promise2
                let x = onFlufilled(this.value);
                resolvePromise(promise2, x, resolve, reject)
            }
            if (this.state == 'rejected') {
                // This is a big pity. Execute onRejected and pass in the failed value
                onRejected(this.reason);
                let x = onRejected(this.reason);
                resolvePromise(promise2, x, resolve, reject)
            }
            // State is pending
            if (this.state == 'pending') {
                this.onResolvedArr.push(() = > {
                    onFlufilled(this.value);
                    let x = onFlufilled(this.value);
                    resolvePromise(promise2, x, resolve, reject)
                })
                this.onRejectedArr.push(() = > {
                    onRejected(this.reason);
                    let x = onRejected(this.reason);
                    resolvePromise(promise2, x, resolve, reject)
                })
            }
        })
        returnpromise2; }}Copy the code
5-6 Complete the resolvePromise function

Making different promise codes work with each other is called resolvePromise

  • If x === promise2, a circular reference is created and waits for completion. A circular reference error is reported
let p = new Promise(resolve= > {
  resolve(0);
});
var p2 = p.then(data= > {
  // Circular reference, waiting for their own completion, a lifetime
  return p2;
})
Copy the code

1

  • Otherwise, if x is an object or function,Let then be x.then
  • X cannot be null
  • X is a normal value. Resolve (x)
  • X is an object or function (including promise),let then = x.then2. When x is an object or function (default promise)
  • The statement then
  • Reject () if ‘then’ fails, reject()
  • If then is a function, then is executed with call, taking this as the first argument, followed by a successful callback and a failed callback
  • If the successful callback is still pormise, the recursion continues. 3. Success and failure can only be called one, so set a called to prevent multiple calls
function resolvePromise(promise2, x, resolve, reject){
  // Loop reference error
  if(x === promise2){
    / / reject an error
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  // Prevent multiple calls
  let called;
  // x is not null and x is an object or function
  if(x ! =null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // A+ specifies the then method that declares then = x
      let then = x.then;
      // If then is a function, the default is Promise
      if (typeof then === 'function') { 
        // Let then execute the first argument this, followed by a successful callback and a failed callback
        then.call(x, y= > {
          // Only one can be called for success or failure
          if (called) return;
          called = true;
          // the result of resolve is still a promise
          resolvePromise(promise2, y, resolve, reject);
        }, err= > {
          // Only one can be called for success or failure
          if (called) return;
          called = true;
          reject(err);// If you fail, you fail})}else {
        resolve(x); // You can do just that}}catch (e) {
      // Also a failure
      if (called) return;
      called = true;
      // Then is not validreject(e); }}else{ resolve(x); }}Copy the code
5-7 Solve other problems

Ondepressing and onRejected are both optional parameters. If they are not functions, they must be ignored

  • Ondepressing returns a normal value, which equals directly when successfulvalue => value
  • OnFulfilled returns a common value. If value => value, then the onFulfilled will run into the next onFulfilled, so there will be a mistakereason => throw err2,secretOnFulfilled or onRejected cannot be called synchronously and must be called asynchronously. We’ll use setTimeout to solve the asynchronous problem
  • This is a pity or onFulfilled. If onFulfilled or onRejected fails, you can return reject().
class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = value= > {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach(fn= >fn()); }};let reject = reason= > {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn= >fn()); }};try{
      executor(resolve, reject);
    } catch(err) { reject(err); }}then(onFulfilled,onRejected) {
    // onFulfilled if this is not a function, ignore onFulfilled and return value
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value;
    // onRejected if it is not a function, just ignore onRejected and throw an error
    onRejected = typeof onRejected === 'function' ? onRejected : err= > { throw err };
    let promise2 = new Promise((resolve, reject) = > {
      if (this.state === 'fulfilled') {
        / / asynchronous
        setTimeout(() = > {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) { reject(e); }},0);
      };
      if (this.state === 'rejected') {
        / / asynchronous
        setTimeout(() = > {
          // If an error is reported
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) { reject(e); }},0);
      };
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() = > {
          / / asynchronous
          setTimeout(() = > {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) { reject(e); }},0);
        });
        this.onRejectedCallbacks.push(() = > {
          / / asynchronous
          setTimeout(() = > {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) { reject(e); }},0)}); }; });// Return promise, complete the chain
    returnpromise2; We can't help but use promises in our work to address asynchronous callbacks. Many libraries or plug-ins you use use promises such as Axios, FETCH, etc. But do you know how promise comes out? Here's the promisesA+ spec, which is cheap10I sold you the yuan.1,PromiseHandwritten statementPromiseLink: HTTPS://juejin.cn/post/6844903625769091079
Copy the code