This article uses ES6 syntax to implement the basic functionality of Promise, suitable for javascript and ES6 based readers, if not, please read

  • http://es6.ruanyifeng.com/

The callback function

In daily development, ajax is often used to request data, get it, and process it.

However, suppose you need to make multiple Ajax requests for data, and each Ajax request is made again based on the previous request data as an argument, the code might look like this:

function dataAjax() {
    $.ajax({
        url: '/woCms/getData'.type: 'get',
        success: function(data) { reChargeAjax(data); }}); }function reChargeAjax(data) {
    $.ajax({
        url: '/woCms/reCharge'.type: 'post',
        data: { data },
        success: function(data) { loginAjax(data); }}); }...Copy the code

Obviously, there are some disadvantages to writing this way:

  • If there are multiple operations and layers of dependency, the nesting can be multiple, and the returned data needs to be processed each time, which makes debugging difficult, the code is not very intuitive, and increases the difficulty of later maintenance
  • This nesting of layers of functions, also known as callback regions, was developed to address the drawbacks of this form and to support multiple concurrent requests

What is the Promise

  • Promises are a means of control for an asynchronous process.
  • Promise objects allow us to handle asynchronous process operations in a more rational and disciplined manner.

Promise basic usage

let pro = new Promise(function (resolve, reject) {
});
Copy the code
  • A Promise is a global object, that is, a class
  • You can create a Promise instance with the new keyword
  • The Promise constructor passes an anonymous function that takes two arguments: resolve and reject
  • Both parameters are methods, and the resolve method handles the result of asynchronous success
  • Reject Indicates the cause of asynchronous operation failure

Three states of Promise

  • Pending: Promise Indicates the default initialization status after creation
  • Depressing: When the resolve method is called, the operation is successful
  • When the Rejected: reject method is invoked, the operation fails

This state can only be reversed from the initialization -> success or the initialization -> failure, and can not be reversed between the success pity and the failure rejected.

Promise class structure

Ok, so now that we know the basic definition and syntax of Promise, let’s simulate the constructor and internal implementation of Promise in code

Class Promise {// constructor; constructor(executor) {this.status ="pending"; // Promise initializes pending this.value = undefined; // This. Reason = undefined; // New Promise((resolve,reject)=>{});letResolve = data => {// The success state function can be changed only when the promise state is pending. // The success or failure state function can be called only when the promise state is pendingif (this.status === "pending") {
                this.status = "fulfilled"; this.value = data; }}let reject = err => {
            if (this.status === "pending") {
                this.status = "rejected";
                this.reason = err;
            }
        }
    }
    executor(resolve, rejcte);
}
Copy the code

Pass resolve and rejcte into the anonymous function of Promise, which correspond to the processing of success and failure respectively. According to the Promise specification, only when the state of a Promise is in the initial state can its state be changed to success or failure, and can only be changed to success or failure.

Then method

Now that we know about Promise creation and state, let’s look at a very important method in promises: the then method

The then() method is used to process a program after an operation

pro.then(function(res) {// Operation successful processing},function(error) {// Handling of operation failure});Copy the code

So how do we define the then function?

Obviously, then is a method on the Promise prototype that takes two anonymous functions as arguments, the first a success function and the second a failure function.

That is, when the Promise state becomes successful, the first function is executed. When the state becomes failed, the second function is executed. So the then function can be defined like this:

then(onFulFiled, onRejected) {// Promise Returns a successful stateif (this.status === "fulfilled"OnFulFiled (this.value) {// The data returned when the promise is successfully executed is the parameter of the success state function onFulFiled(this.value); }if (this.status === "rejected"// Promise returns the failed function onRejected(this.reason); // Promise returns the failed function onRejected(this.reason); }}Copy the code

Promise then asynchronous code queue execution

But let’s look at this Promise code:

let pro = new Promise((resolve, reject) => {
    setTimeout(function () {
        resolve("suc"); }, 1000); }); // Call the same Promise instance multiple timesthenFunction pro.then(data => {console.log("date", data);
}, err => {
    console.log("err", err);
});
pro.then(data => {
    console.log("date", data);
}, err => {
    console.log("err", err); }) The result is date suc date SUCCopy the code

Then the Promise instance calls the success or failure state function asynchronously, and the Promise instance calls the THEN method multiple times. We can see the output of the multiple success state function at the end:

  • The state function is executed asynchronously in a Promise, but the then function of a Promise is still pending
  • The Promise instance Pro calls the THEN function multiple times and still executes the state function when the state is pending

How does this logic handle pending state in the THEN function?

In publish-subscribe mode, the state function is stored in a queue and then retrieved when called.

Class Promise{constructor(executor){constructor(constructor){// Use publish-subscribe to queue state functions when asynchronous code in a Promise encapsulates success or failure state functions. This. OnFuiFiledAry = []; this.onRejectedAry = [];let resolve = data => {
            if (this.status === "pending") {... / / when the state function in the queue have a storage function, take out and perform, queue is a function of inside this. OnFulFiledAry. ForEach (fn = > fn ()); }}let reject = err => {
            if (this.status === "pending") {... this.onRejectedAry.forEach(fn => fn()); }}}then(onFuiFiled, onRejected) {
        ...
        if (this.status === "pending") { this.onFuiFiledAry.push(() => { onFuiFiled(this.value); }); this.onRejectedAry.push(() => { onRejected(this.reason); }); }}}Copy the code

The Promise then function returns a new Promise

How does jQuery implement chain calls? Yes, this, jQuery makes chained calls by returning a reference to the current object, this, after the function has been executed.

Let’s take a look at an example of what happens if Promise implements a chained call using this.

let pro = new Promise(function (resolve, reject) {
    resolve(123);
});
let p2 = pro.then(function() {return this, p and P2 are the same promise, and P2 should be in a successful statethenThrow new Error("error");
});
p2.then(function (data) {
    console.log("promise success", data);
}, function(err) {// An error was caught at this time, indicating that it is not the same promise, because the state console.log() cannot be changed after the promise state becomes successful."promise or this:", err);
})
Copy the code

P2 is already in a failed state when an Error is raised, so it will proceed to the next failed state function for THEN. This is the result, indicating that Promise is not implemented through this.

So how does the chain call in Promise work?

As a result, a new Promise is returned.

then(onFuiFiled, onRejected) {
    let newpro;
    if (this.status === "fulfilled") {
        newpro = new Promise((resolve, reject) => {
            onFuiFiled(this.value);
        });
    }
    if (this.status === "rejected") {
        newpro = new Promise((resolve, reject) => {
            onRejected(this.reason);
        });
    }
    if (this.status === "pending") {
        newpro = new Promise((resolve, reject) => {
            this.onFuiFiledAry.push(() => {
                onFuiFiled(this.value);
            });
            this.onRejectedAry.push(() => {
                onRejected(this.reason);
            });
        });
    }
    return newpro;
}
Copy the code

The Promise then function returns value resolution

Let’s move on to the THEN function. The THEN function accepts two anonymous functions. Suppose the THEN function returns a number, an object, a function, or a promise.

Let’s start with an example:

let pro = new Promise((resolve, reject) => {
    resolve(123);
});
pro.then(data => {
    console.log("then-1",data);
    return 1;
}).then(data => {
    console.log("then-2", data);
});
Copy the code

Example output is then-1 123 then-2 1

That is, promises are resolved according to the different results returned when the then state function is executed:

  • If the previous THEN function returns a value, an object, or a function, then the value and object are returned directly to the next THEN function.
  • If then returns NULL, the next THEN returns NULL.
  • If then returns a new promise, the state function of the new promise is used to determine which state function to call next then. If the returned promise does not perform any state function, the state of the promise is pending

So how do we do that? Let’s look at the code

functionAnalysisPromise (Promise, res, resolve, reject) {// The promise instance itself is returned without calling any state function methodsif (promise === res) {
        return reject(new TypeError("Recycle")); } // res is not null, res is an object or functionif(res ! == null && (typeof res ==="object" || typeof res === "function")) {
        try {
            let then= res.then; // Prevent object.defineProperty from being defined as {then: {}}if (typeof then= = ="function"Then. Call (res, y => {// y => {// y => { AnalysisPromise (promise, y, resolve, reject); }, err => { reject(err); })}else{/ / herethenIs a common object, then directly call the nextthenResolve (res) is output as a parameter. } } catch (error) { reject(error); }}else{// res is the value resolve(res); }}then(onFuiFiled, onRejected) {
    let newpro;
    if (this.status === "fulfilled") {
        newpro = new Promise((resolve, reject) => {
            letres = onFuiFiled(this.value); analysisPromise(newpro, res, resolve, reject); }); }...return newpro;
}
Copy the code

The analysisPromise function is used to parse the return value of the THEN function in the Promise, and must be processed when the state function in the then function returns the result value.

The then function in a Promise is asynchronous

Now, let’s look at an example of Promise:

let pro = new Promise((resolve, reject) => {
    resolve(123);
});
pro.then(data => {
    console.log(1);
});
console.log(2);
Copy the code

The output is 2, 1

So, 2 is printed first, and 1 is printed after the state function in the then function, indicating that the synchronous code is executed before the asynchronous code in the then function

So how do we implement this part of the code?

Using the setTimeout function, add the setTimeout function code to each anonymous function in the THEN function, like this:

then(onFuiFiled, onRejected) {
    let newpro;
    if (this.status === "fulfilled") {
        newpro = new Promise((resolve, reject) => {
            setTimeout(() => {
                try {
                    letres = onFuiFiled(this.value); analysisPromise(newpro, res, resolve, reject); } catch (error) { reject(error); }}); }); }...return newpro;
}
Copy the code

Well, about the general implementation of Promise on the first analysis here, I hope to help you, if there are mistakes in the article, welcome to correct

The article mainly refers to specific learning resources

  • http://es6.ruanyifeng.com/
  • http://liubin.org/promises-book/#es6-promises