Home Web Front-end JS Tutorial Handwritten JS to implement Promise

Handwritten JS to implement Promise

May 07, 2020 am 09:17 AM
js promise

Promise Overview

Promise is a solution for managing asynchronous programming. It is a constructor that can be used to create an instance with new each time; it has three states: pending, fulfilled and rejected , these three states will not be affected by the outside world. The state can only change from pending to fullfilled (success), pending to rejected (failure), and once changed, it will not change again. After the status changes, it will return successful The result or the reason for failure, it throws resolve, reject, catch, finally, then, all, race, done. In the latest proposal, the allSettled method is added, which will return regardless of success or failure. Next, we Implement the entire Promise yourself

executor function

We know that when a Promise instance is created, the executor function will be executed immediately. The executor function passes two parameters, resolve and reject. , if the executor function executes incorrectly, the Promise instance status will change to rejected

class MyPromise{
    constructor(executor) {
        this.status = "pending";     // 初始化状态为pending
        this.value = undefined;      // 初始化返回的成功的结果或者失败的原因
        
        // 这里是resolve方法,成功后执行,将状态改变为resolved,并且将结果返回
        let resolve = result => {
            if(this.status !== "pending") return;  // 状态一旦改变,就不会再变
            this.status = "resolved";
            this.value = result;
        }
        
        // 这里是reject方法,异常时执行,状态改为rejected,并且将失败的原因返回
        let reject = reason => {
            if(this.status !== "pending") return;
            this.status = "rejected";
            this.value = reason;
        }
        // try、catch捕获异常,如果错误,执行reject方法
        try {
            executor(resolve, reject)
        } catch(err) {
            reject(err)
        }
    }
}
Copy after login

Let’s verify what the current Promise looks like

let p1 = new MyPromise((resolve, reject) => {
    resolve(1);
})
let p2 = new MyPromise((resolve, reject) => {
    reject(2);
})
console.log(p1);
console.log(p2);
Copy after login

You can see that the status has changed, inside The value of is also the result of success and the cause of failure. The then method has two parameters. The first parameter is executed when successful, and the second parameter is executed after failure. The chain call of then is the same as the array, etc., and a Promise instance will be returned after each execution. If after success, the successful function in the first then is null, it will continue to search downward until the function that is not null is executed. The result returned in the previous then will directly affect whether the next then succeeds or fails. Function, after understanding these, let's try to implement it~

then method

then(resolveFn, rejectFn) {
    // 如果传入的两个参数不是函数,则直接执行返回结果
    let resolveArr = [];
    let rejectArr = [];
    
    if(typeof resolveFn !== "function") {
        resolveFn = result => {
            return result;
        }
    }
    
    if(typeof rejectFn !== "function") {
        rejectFn = reason => {
            return MyPromise.reject(reason);
        }
    }
    
    return new Mypromise((resolve, reject) => {
        resolveArr.push(result => {
            try {
                let x = resolveFn(result);
                
                if(x instanceof MyPromise) {
                    x.then(resolve, reject)
                    return;
                }
                
                resolve(x);
            } catch(err) {
                reject(err)
            }
        })
        
        rejectArr.push(reason => {
            try {
                let x = rejectFn(reason);
                
                if(x instanceof MyPromise) {
                    x.then(resolve, reject)
                    return;
                }
                
                resolve(x);
            } catch(err) {
                reject(err)
            }
        })
    })
}
Copy after login

Let's sort out the above code

class MyPromise{
    constructor(executor) {
        this.status = "pending";     // 初始化状态为pending
        this.value = undefined;      // 初始化返回的成功的结果或者失败的原因
        this.resolveArr = [];        // 初始化then中成功的方法
        this.rejectArr = [];         // 初始化then中失败的方法
        
        
        // 定义change方法,因为我们发现好像resolve和reject方法共同的地方还挺多
        let change = (status, value) => {
            if(this.status !== "pending") return;  // 状态一旦改变,就不会再变
            this.status = status;
            this.value = value;
            
            // 根据状态判断要执行成功的方法或失败的方法
            let fnArr = status === "resolved" ? this.resolveArr : this.rejectArr;
            
            // fnArr中的方法依次执行
            fnArr.forEach(item => {
                if(typeof item !== "function") return;
                item(this. value);
            })
        }
        // 这里是resolve方法,成功后执行,将状态改变为resolved,并且将结果返回
        let resolve = result => {
            change("resolved", result)
        }
        
        // 这里是reject方法,异常时执行,状态改为rejected,并且将失败的原因返回
        let reject = reason => {
            change("rejected", reason);
        }
        
        // try、catch捕获异常,如果错误,执行reject方法
        try {
            executor(resolve, reject)
        } catch(err) {
            reject(err)
        }
    }
    
    then(resolveFn, rejectFn) {
    // 如果传入的两个参数不是函数,则直接执行返回结果
    
        if(typeof resolveFn !== "function") {
            resolveFn = result => {
                return result;
            }
        }
        
        if(typeof rejectFn !== "function") {
            rejectFn = reason => {
                return MyPromise.reject(reason);
            }
        }
        
        return new MyPromise((resolve, reject) => {
            this.resolveArr.push(result => {
                try {
                    let x = resolveFn(result);  // 获取执行成功方法返回的结果
                    
                    // 如果x是一个promise实例,则继续调用then方法 ==> then链的实现
                    if(x instanceof MyPromise) {
                        x.then(resolve, reject)
                        return;
                    }
                    
                    // 不是promise实例,直接执行成功的方法
                    resolve(x);
                } catch(err) {
                    reject(err)
                }
            })
            
            this.rejectArr.push(reason => {
                try {
                    let x = rejectFn(reason);
                    
                    if(x instanceof MyPromise) {
                        x.then(resolve, reject)
                        return;
                    }
                    
                    resolve(x);
                } catch(err) {
                    reject(err)
                }
            })
        })
    }
}
Copy after login

Let's take a look The effect

new MyPromise((resolve, reject) => {
    resolve(1);
}).then(res => {
    console.log(res, 'success');
}, err => {
    console.log(err, 'error');
})
Copy after login

At this time, the problem occurred. We found that nothing seemed to be output. What if we made a small change to the above test example?

new MyPromise((resolve, reject) => {
    setTimeout(_ => {
        resolve(1);
    }, 0)
}).then(res => {
    console.log(res, 'success');    // 1 "success"
}, err => {
    console.log(err, 'error');
})
Copy after login

This is because the executor function is executed immediately after the Promise instance is created, and the then method has not been executed yet, so the array will be empty regardless of success or failure. Then you may have questions again, why does it work just fine after adding setTimeout? This is because in the event queue mechanism, setTimeout will be put into the event queue and will be executed after the main thread is completed. At this time, the then method will store the successful or failed function, so whether it is a successful array or a failed array, There is already a value, and it will be complete if you execute it at this time~

But we cannot write setTimeout as a solution when using it. Since we are encapsulating, we must solve the problem within the encapsulated function. According to With this idea, we can also determine whether there is a value in the array when the resolve and reject methods are executed. If not, we can use setTimeout to delay its execution. The code is as follows~

// 这里是resolve方法,成功后执行,将状态改变为resolved,并且将结果返回
let resolve = result => {   
    // 如果数组中有值,则立即改变状态
    if(this.resolveArr.length > 0) {
        change("resolved", result)
    }
    // 如果没值,则延后改变状态
    let timer = setTimeout(_ => {
        change("resolved", result)
        clearTimeout(timer);
    }, 0)
}
// 这里是reject方法,异常时执行,状态改为rejected,并且将失败的原因返回
let reject = reason => {
// 如果数组中有值,则立即改变状态
    if(this.rejectArr.length > 0) {
        change("rejected", reason);
    }
    // 如果没值,则延后改变状态
    let timer = setTimeout(_ => {
        change("rejected", reason);
        clearTimeout(timer);
    }, 0)
}
Copy after login

Now we will Try it

// 1、已经成功了
new MyPromise((resolve, reject) => {
    resolve('我成功啦,吼吼吼~~~~');            
    reject('我都已经成功了,你别想让我失败,哼~~');
}).then(res => {
    console.log(res, 'success');         // 我成功啦,吼吼吼~~~~ success
}, err => {
    console.log(err, 'error');
})
// 2、先失败了
new MyPromise((resolve, reject) => {
    reject('失败了,我好委屈,呜呜呜~~');
    resolve('已经失败了~~~');            
}).then(res => {
    console.log(res, 'success');         
}, err => {
    console.log(err, 'error');          // 失败了,我好委屈,呜呜呜~~ error
})
// 3、链式调用
new MyPromise((resolve, reject) => {
    reject('失败了,我好委屈,呜呜呜~~');
    resolve('已经失败了~~~');            
}).then(res => {
    console.log(res);
}, err => {
    console.log(err, 'error');          // 失败了,我好委屈,呜呜呜~~ error
    return '我要发奋图强,不会被困难所击倒,我要成功!!!'
}).then(res1 => {
    console.log(res1, '经过不懈努力,我终于在第二次成功了~');  // 我要发奋图强,不会被困难所击倒,我要成功!!!  经过不懈努力,我终于在第二次成功了~
}, err1 => {
    console.log(err1, '第二次失败');
})
Copy after login

This perfectly solves the problem that the then method will not be executed during the first call. At the same time, chained calls are implemented. Regarding the chained calls, I will say a few more words. In fact, regardless of the chained calls of the array, it is because this instance was returned last time.

catch method

The catch method is to catch exceptions, which is the same as the second callback function of the then method

catch(rejectFn) {
    return this.then(null, rejectFn)
}
Copy after login

resolve Method

We know that Promsie can also be used like this

let p1 = MyPromise.resolve(1);
console.log(p1);
Copy after login

We expect such a way of writing, but now an error will definitely be thrown: MyPromise.resolve is not a method

Now we need to encapsulate the resolve method. What we need to make clear is that after resolve, Promise supports continuing to call then in a chain. Therefore, we need to execute the resolve method and return a Promise instance

static resolve(result) {
    // 返回新的promise实例,执行promise实例中resolve方法
    return new MyPromise(resolve => {
        resolve(result)
    })
}
Copy after login

reject method

Like the resolve method, except that it receives the failed function

static reject(reason) {
    // 返回新的promise实例,执行promise实例中reject方法
    return new MyPromise((_, reject) => {
        reject(reason);
    })
}
Copy after login

done method

ES6 In the standard introduction book, the explanation of the done method is as follows: No matter the callback chain of the Promise object ends with the then method or the catch method, as long as the last method throws an error, it may not be caught. For this purpose, Promise provides a done method, which is always at the end of the callback chain and is guaranteed to throw any errors that may occur. Okay, we know what this method does, let’s start writing now ~

done(resolveFn, rejectFn) {
    this.then(resolveFn, rejectFn)
        .catch(reason => {
            setTimeout(() => {
                throw reason;
            }, 0)
        })
}
Copy after login

It can receive callback functions in fulfilled and rejected states, or it can not provide any parameters. But no matter what, the done method will catch any possible errors and throw them to the global

finally method

The finally method will be executed regardless of success or failure. Methods, methods like this and complete methods in small programs, etc., let’s try to implement it~

finally(finallyFn) {
    let P = this.constructor;
    return this.then(
        value => P.resolve(finallyFn()).then(() => value),
        reason => P.reject(finallyFn()).then(() => reason)
    )
}
Copy after login

Let’s verify it

new MyPromise((resolve, reject) => {
    reject('失败了,我好委屈,呜呜呜~~');
    resolve('已经失败了~~~');
}).then(res => {
    console.log(res);
}, err => {
    console.log(err, 'error');          // 失败了,我好委屈,呜呜呜~~ error
    return '我要发奋图强,不会被困难所击倒,我要成功!!!'
}).finally(() => {
    console.log('执行了吗');            // 这里会输出"执行了吗"
})
Copy after login

all methods

The all method receives an array and will return when each instance in the array succeeds. It also returns an array. Each parameter is the result returned by the corresponding promise. If one item fails, the all method will Return failure

// 接收数组参数
static all(promiseList) {
    // 返回新实例,调用后还可使用then、catch等方法
    return new MyPromise((resolve, reject) => {
        let index = 0,      // 成功次数计数
            results = [];   // 返回的结果
        
        for(let i = 0; i < promiseList.length; i++) {
            let item = promiseList[i];
            
            // 如果item不是promise实例
            if(!(item instanceof MyPromise)) return;
            
            item.then(result => {
                index++;
                results[i] = result;
                if(index === promiseList.length) {
                    resolve(results);
                }
            }).catch(reason => {
                reject(reason);
            })
        }
    })
}
Copy after login

Let’s verify

// 1.有失败的情况
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.resolve(3);
MyPromise.all([p1, p2, p3])
    .then(res => {
        console.log(res);
    }).catch(err => {
        console.log(err, &#39;err&#39;);     // 2 "err"
    })
// 2.无失败的情况
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.resolve(2);
let p3 = MyPromise.resolve(3);
MyPromise.all([p1, p2, p3])
    .then(res => {
        console.log(res, &#39;success&#39;);   // [1, 2, 3] "success"
    }).catch(err => {
        console.log(err, &#39;err&#39;);
    })
Copy after login

race method

race方法同样接收一个数组参数,里面每一项是Promise实例,它返回最快改变状态的Promise实例方法的结果

static race(promiseList) {
    return new MyPromise((resolve, reject) => {
        promiseList.forEach(item => {
            if(!(item instanceof MyPromise)) return;
            
            item.then(result => {
                resolve(result);
            }).catch(err => {
                reject(err)
            })
        })
    })
}
复制代码验证
// 1.
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res);            // 1 &#39;success&#39;
    }).catch(err => {
        console.log(err, &#39;err&#39;);    
    })
// 2.
let p1 = MyPromise.reject(1);
let p2 = MyPromise.resolve(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res, &#39;success&#39;);   
    }).catch(err => {
        console.log(err, &#39;err&#39;);       // 1 &#39;err&#39;
    })
    
// 3.
let p1 = MyPromise.reject(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.reject(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res, &#39;success&#39;);   
    }).catch(err => {
        console.log(err, &#39;err&#39;);       // 1 &#39;err&#39;
    })
Copy after login

尝试实现allSettled方法

allSettled方法也是接收数组参数,但是它无论成功或者失败,都会返回

static allSettled(promiseList) {
    return new MyPromise((resolve, reject) => {
        let results = [];
        
        for(let i = 0; i < promiseList.length; i++) {
            let item = promiseList[i];
            
            if(!(item instanceof MyPromise)) return;
            
            item.then(result => {
                results[i] = result;
            }, reason => {
                results[i] = reason;
            })
            resolve(results);
        }
    })
}
复制代码验证
// 1.
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.resolve(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res);            // [1, 2, 3] &#39;success&#39;
    }).catch(err => {
        console.log(err, &#39;err&#39;);    
    })
// 2.
let p1 = MyPromise.reject(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.reject(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res, &#39;success&#39;);   // [1, 2, 3] &#39;success&#39;
    }).catch(err => {
        console.log(err, &#39;err&#39;);       
    })
    
// 3.
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
    .then(res => {
        console.log(res, &#39;success&#39;);   // [1, 2, 3] &#39;success&#39;
    }).catch(err => {
        console.log(err, &#39;err&#39;);       
    })
Copy after login

推荐教程:《JS教程

The above is the detailed content of Handwritten JS to implement Promise. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Roblox: Bubble Gum Simulator Infinity - How To Get And Use Royal Keys
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
Nordhold: Fusion System, Explained
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
Mandragora: Whispers Of The Witch Tree - How To Unlock The Grappling Hook
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1666
14
PHP Tutorial
1273
29
C# Tutorial
1253
24
Recommended: Excellent JS open source face detection and recognition project Recommended: Excellent JS open source face detection and recognition project Apr 03, 2024 am 11:55 AM

Face detection and recognition technology is already a relatively mature and widely used technology. Currently, the most widely used Internet application language is JS. Implementing face detection and recognition on the Web front-end has advantages and disadvantages compared to back-end face recognition. Advantages include reducing network interaction and real-time recognition, which greatly shortens user waiting time and improves user experience; disadvantages include: being limited by model size, the accuracy is also limited. How to use js to implement face detection on the web? In order to implement face recognition on the Web, you need to be familiar with related programming languages ​​and technologies, such as JavaScript, HTML, CSS, WebRTC, etc. At the same time, you also need to master relevant computer vision and artificial intelligence technologies. It is worth noting that due to the design of the Web side

How to create a stock candlestick chart using PHP and JS How to create a stock candlestick chart using PHP and JS Dec 17, 2023 am 08:08 AM

How to use PHP and JS to create a stock candle chart. A stock candle chart is a common technical analysis graphic in the stock market. It helps investors understand stocks more intuitively by drawing data such as the opening price, closing price, highest price and lowest price of the stock. price fluctuations. This article will teach you how to create stock candle charts using PHP and JS, with specific code examples. 1. Preparation Before starting, we need to prepare the following environment: 1. A server running PHP 2. A browser that supports HTML5 and Canvas 3

Essential tools for stock analysis: Learn the steps to draw candle charts with PHP and JS Essential tools for stock analysis: Learn the steps to draw candle charts with PHP and JS Dec 17, 2023 pm 06:55 PM

Essential tools for stock analysis: Learn the steps to draw candle charts in PHP and JS. Specific code examples are required. With the rapid development of the Internet and technology, stock trading has become one of the important ways for many investors. Stock analysis is an important part of investor decision-making, and candle charts are widely used in technical analysis. Learning how to draw candle charts using PHP and JS will provide investors with more intuitive information to help them make better decisions. A candlestick chart is a technical chart that displays stock prices in the form of candlesticks. It shows the stock price

How to use JS and Baidu Maps to implement map pan function How to use JS and Baidu Maps to implement map pan function Nov 21, 2023 am 10:00 AM

How to use JS and Baidu Map to implement map pan function Baidu Map is a widely used map service platform, which is often used in web development to display geographical information, positioning and other functions. This article will introduce how to use JS and Baidu Map API to implement the map pan function, and provide specific code examples. 1. Preparation Before using Baidu Map API, you first need to apply for a developer account on Baidu Map Open Platform (http://lbsyun.baidu.com/) and create an application. Creation completed

How to use JS and Baidu Map to implement map click event processing function How to use JS and Baidu Map to implement map click event processing function Nov 21, 2023 am 11:11 AM

Overview of how to use JS and Baidu Maps to implement map click event processing: In web development, it is often necessary to use map functions to display geographical location and geographical information. Click event processing on the map is a commonly used and important part of the map function. This article will introduce how to use JS and Baidu Map API to implement the click event processing function of the map, and give specific code examples. Steps: Import the API file of Baidu Map. First, import the file of Baidu Map API in the HTML file. This can be achieved through the following code:

How to use JS and Baidu Maps to implement map heat map function How to use JS and Baidu Maps to implement map heat map function Nov 21, 2023 am 09:33 AM

How to use JS and Baidu Maps to implement the map heat map function Introduction: With the rapid development of the Internet and mobile devices, maps have become a common application scenario. As a visual display method, heat maps can help us understand the distribution of data more intuitively. This article will introduce how to use JS and Baidu Map API to implement the map heat map function, and provide specific code examples. Preparation work: Before starting, you need to prepare the following items: a Baidu developer account, create an application, and obtain the corresponding AP

Keeping your word: The pros and cons of delivering on your promises Keeping your word: The pros and cons of delivering on your promises Feb 18, 2024 pm 08:06 PM

In daily life, we often encounter problems between promises and fulfillment. Whether in a personal relationship or a business transaction, delivering on promises is key to building trust. However, the pros and cons of commitment are often controversial. This article will explore the pros and cons of commitments and give some advice on how to keep your word. The promised benefits are obvious. First, commitment builds trust. When a person keeps his word, he makes others believe that he is a trustworthy person. Trust is the bond established between people, which can make people more

PHP and JS Development Tips: Master the Method of Drawing Stock Candle Charts PHP and JS Development Tips: Master the Method of Drawing Stock Candle Charts Dec 18, 2023 pm 03:39 PM

With the rapid development of Internet finance, stock investment has become the choice of more and more people. In stock trading, candle charts are a commonly used technical analysis method. It can show the changing trend of stock prices and help investors make more accurate decisions. This article will introduce the development skills of PHP and JS, lead readers to understand how to draw stock candle charts, and provide specific code examples. 1. Understanding Stock Candle Charts Before introducing how to draw stock candle charts, we first need to understand what a candle chart is. Candlestick charts were developed by the Japanese

See all articles