Home > Web Front-end > JS Tutorial > body text

Handwritten JS to implement Promise

Guanhui
Release: 2020-05-07 09:27:21
forward
2778 people have browsed it

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!

Related labels:
source:juejin.im
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
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!