Maison > interface Web > js tutoriel > JS manuscrit pour implémenter Promise

JS manuscrit pour implémenter Promise

Guanhui
Libérer: 2020-05-07 09:27:21
avant
2823 Les gens l'ont consulté

Présentation de Promise

Promise est une solution de gestion de la programmation asynchrone C'est un constructeur qui peut créer une instance avec new à chaque utilisation il a trois états : en attente, accompli et rejeté, ces trois états ne seront pas affectés par le monde extérieur. L'état ne peut passer que d'en attente à accompli (succès), en attente à rejeté (échec), et une fois modifié, il ne changera plus après le changement de statut. , il renverra succès Le résultat ou la raison de l'échec, il lance résoudre, rejeter, attraper, enfin, puis, tout, course, terminé Dans la dernière proposition, la méthode allSettled est ajoutée, qui reviendra quel que soit le succès ou l'échec. .Ensuite, nous implémentons vous-même l'intégralité de la promesse

fonction d'exécution

Nous savons que lorsqu'une instance de promesse est créée, la fonction d'exécution sera exécutée immédiatement. passe deux paramètres, résoudre et rejeter. , si la fonction d'exécution ne s'exécute pas correctement, le statut de l'instance Promise deviendra rejeté

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)
        }
    }
}
Copier après la connexion

Vérifions à quoi ressemble la promesse actuelle

let p1 = new MyPromise((resolve, reject) => {
    resolve(1);
})
let p2 = new MyPromise((resolve, reject) => {
    reject(2);
})
console.log(p1);
console.log(p2);
Copier après la connexion

Vous pouvez le voir. le statut a changé, à l'intérieur La valeur de est aussi le résultat du succès et la cause de l'échec. La méthode then a deux paramètres. Le premier paramètre est exécuté en cas de succès et le deuxième paramètre est exécuté après un échec. L'appel en chaîne de then est le même que celui du tableau, etc., et une instance Promise sera renvoyée après chaque exécution. Si après le succès, la fonction réussie dans la première est alors nulle, elle continuera à rechercher vers le bas jusqu'à ce que la fonction qui n'est pas nulle soit exécutée. Le résultat renvoyé dans la précédente affectera alors directement si la fonction suivante réussit ou échoue. après avoir compris cela, essayons de l'implémenter ~

puis la méthode

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)
            }
        })
    })
}
Copier après la connexion

Traitons le code ci-dessus

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)
                }
            })
        })
    }
}
Copier après la connexion

Jetons un coup d'oeil L'effet

new MyPromise((resolve, reject) => {
    resolve(1);
}).then(res => {
    console.log(res, 'success');
}, err => {
    console.log(err, 'error');
})
Copier après la connexion

À ce moment-là, le problème s'est produit. Nous avons constaté que rien ne semblait sortir. Et si nous apportions une petite modification à l'exemple de test ci-dessus ?

new MyPromise((resolve, reject) => {
    setTimeout(_ => {
        resolve(1);
    }, 0)
}).then(res => {
    console.log(res, 'success');    // 1 "success"
}, err => {
    console.log(err, 'error');
})
Copier après la connexion

Cela est dû au fait que la fonction d'exécution est exécutée immédiatement après la création de l'instance Promise et que la méthode then n'a pas encore été exécutée, le tableau sera donc vide quel que soit le succès ou l'échec. Ensuite, vous aurez peut-être à nouveau des questions, pourquoi cela fonctionne-t-il très bien après l'ajout de setTimeout ? En effet, dans le mécanisme de file d'attente d'événements, setTimeout sera placé dans la file d'attente d'événements et sera exécuté une fois le thread principal terminé. À ce moment-là, la méthode then stockera la fonction réussie ou échouée, donc s'il s'agit d'un tableau réussi. ou un tableau défaillant, il a déjà une valeur, et il sera complet si vous l'exécutez à ce moment ~

Mais nous ne pouvons pas écrire setTimeout comme solution lorsque nous l'utilisons Puisque nous encapsulons, nous devons résoudre. le problème dans la fonction encapsulée. Selon cette idée, nous pouvons également déterminer s'il y a une valeur dans le tableau lorsque les méthodes de résolution et de rejet sont exécutées. Sinon, nous pouvons utiliser setTimeout pour retarder son exécution. suit~

// 这里是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)
}
Copier après la connexion

Maintenant, nous essayons

// 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, '第二次失败');
})
Copier après la connexion

Cela résout parfaitement le problème de la méthode then non exécutée lors du premier appel. Parallèlement, des appels chaînés sont mis en œuvre. Concernant les appels chaînés, je dirai encore quelques mots. En fait, quels que soient les appels chaînés du tableau, c'est parce que cette instance a été renvoyée la dernière fois.

méthode catch

la méthode catch consiste à intercepter les exceptions, c'est la même chose que la deuxième fonction de rappel de la méthode then

catch(rejectFn) {
    return this.then(null, rejectFn)
}
Copier après la connexion

resolve Méthode

Nous savons que Promsie peut également être utilisé de cette manière

let p1 = MyPromise.resolve(1);
console.log(p1);
Copier après la connexion

Nous nous attendons à une telle façon d'écrire, mais maintenant une erreur sera certainement générée : MyPromise.resolve n'est pas a method

Maintenant, nous devons encapsuler la méthode de résolution. Ce que nous devons préciser, c'est qu'après la résolution, Promise prend en charge la poursuite de l'appel dans une chaîne. Par conséquent, nous devons exécuter la méthode de résolution et renvoyer une méthode. L'instance de promesse

static resolve(result) {
    // 返回新的promise实例,执行promise实例中resolve方法
    return new MyPromise(resolve => {
        resolve(result)
    })
}
Copier après la connexion

méthode de rejet

est comme la méthode de résolution, sauf qu'elle reçoit la fonction ayant échoué

static reject(reason) {
    // 返回新的promise实例,执行promise实例中reject方法
    return new MyPromise((_, reject) => {
        reject(reason);
    })
}
Copier après la connexion

méthode done

ES6 Dans le livre d'introduction standard, l'explication de la méthode done est la suivante : Peu importe que la chaîne de rappel de l'objet Promise se termine par la méthode then ou la méthode catch, tant que la dernière méthode lance une erreur, elle risque de ne pas être détectée. À cette fin, Promise fournit une méthode done, qui se trouve toujours à la fin de la chaîne de rappel et qui est garantie de générer toutes les erreurs pouvant survenir. D'accord, nous savons ce que fait cette méthode, commençons à écrire maintenant ~

done(resolveFn, rejectFn) {
    this.then(resolveFn, rejectFn)
        .catch(reason => {
            setTimeout(() => {
                throw reason;
            }, 0)
        })
}
Copier après la connexion

Elle peut recevoir des fonctions de rappel dans les états rempli et rejeté, ou elle ne peut fournir aucun paramètre. Mais quoi qu'il arrive, la méthode done détectera toutes les erreurs possibles et les renverra à la méthode globale

finally

La méthode enfin sera exécutée quel que soit le succès ou l'échec . Méthodes, méthodes comme celle-ci et méthodes complètes dans de petits programmes, etc., essayons de l'implémenter ~

finally(finallyFn) {
    let P = this.constructor;
    return this.then(
        value => P.resolve(finallyFn()).then(() => value),
        reason => P.reject(finallyFn()).then(() => reason)
    )
}
Copier après la connexion

Vérifions-le

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

toutes les méthodes

.

La méthode all reçoit un tableau et sera renvoyée lorsque chaque instance du tableau réussit. Elle renvoie également un tableau. Chaque paramètre est le résultat renvoyé par la promesse correspondante. Si un élément échoue, la méthode all renvoie un échec<🎜. >

// 接收数组参数
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);
            })
        }
    })
}
Copier après la connexion

à vérifier

// 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;);
    })
Copier après la connexion

méthode de course

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;
    })
Copier après la connexion

尝试实现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;);       
    })
Copier après la connexion

推荐教程:《JS教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:juejin.im
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal