Heim > Web-Frontend > js-Tutorial > Handgeschriebenes JS zur Implementierung von Promise

Handgeschriebenes JS zur Implementierung von Promise

Guanhui
Freigeben: 2020-05-07 09:27:21
nach vorne
2800 Leute haben es durchsucht

Promise-Übersicht

Promise ist eine Lösung zur Verwaltung der asynchronen Programmierung. Es handelt sich um einen Konstruktor, der bei jeder Verwendung eine Instanz mit neuem erstellen kann. Erfüllt und abgelehnt. Diese drei Zustände werden von der Außenwelt nicht beeinflusst. Der Status kann nur von „Ausstehend“ zu „Erfüllt“ (Erfolg) und „Ausstehend“ zu „Abgelehnt“ (Fehler) geändert werden , es wird zurückgegeben success Das Ergebnis oder der Grund für den Fehler, es wirft „resolve“, „reject“, „catch“, „finally“, „all“, „race“, „done“ Im neuesten Vorschlag wurde die allSettled-Methode hinzugefügt, die unabhängig von Erfolg oder Misserfolg zurückkehrt Als nächstes implementieren wir das gesamte Promise selbst

Executor-Funktion

Wir wissen, dass die Executor-Funktion sofort ausgeführt wird, wenn eine Promise-Instanz erstellt wird übergibt zwei Parameter: „resolve“ und „reject“. Wenn die Executor-Funktion fehlerhaft ausgeführt wird, ändert sich der Status der Promise-Instanz in „Rejected“ Der Status hat sich im Inneren geändert Der Wert von ist auch das Ergebnis des Erfolgs und die Ursache des Scheiterns. Die then-Methode verfügt über zwei Parameter. Der erste Parameter wird bei Erfolg ausgeführt, der zweite Parameter wird nach einem Fehler ausgeführt. Der Kettenaufruf von then ist derselbe wie das Array usw., und nach jeder Ausführung wird eine Promise-Instanz zurückgegeben. Wenn nach Erfolg die erfolgreiche Funktion im ersten „dann“ null ist, wird die Suche nach unten fortgesetzt, bis die Funktion, die nicht „null“ ist, ausgeführt wird. Das in der vorherigen Funktion zurückgegebene Ergebnis wirkt sich direkt darauf aus, ob die nächste Funktion erfolgreich ist oder fehlschlägt. Nachdem wir diese verstanden haben, versuchen wir, sie zu implementieren ~

dann Methode

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)
        }
    }
}
Nach dem Login kopieren

Lass uns den obigen Code sortieren

let p1 = new MyPromise((resolve, reject) => {
    resolve(1);
})
let p2 = new MyPromise((resolve, reject) => {
    reject(2);
})
console.log(p1);
console.log(p2);
Nach dem Login kopieren
Lass uns einen Blick auf den Effekt werfen

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)
            }
        })
    })
}
Nach dem Login kopieren

Zu diesem Zeitpunkt ist das Problem aufgetreten. Es schien, als würde nichts ausgegeben. Was wäre, wenn wir eine kleine Änderung am obigen Testbeispiel vornehmen würden?

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)
                }
            })
        })
    }
}
Nach dem Login kopieren

Das liegt daran, dass die Executor-Funktion unmittelbar nach der Erstellung der Promise-Instanz ausgeführt wird und die then-Methode noch nicht ausgeführt wurde, sodass das Array unabhängig von Erfolg oder Misserfolg leer ist. Dann haben Sie vielleicht wieder Fragen: Warum funktioniert es nach dem Hinzufügen von setTimeout einwandfrei? Dies liegt daran, dass setTimeout im Ereigniswarteschlangenmechanismus in die Ereigniswarteschlange gestellt und nach Abschluss des Hauptthreads ausgeführt wird. Zu diesem Zeitpunkt speichert die Methode then die erfolgreiche oder fehlgeschlagene Funktion, also ob es sich um ein erfolgreiches Array handelt oder ein fehlgeschlagenes Array. Es hat bereits einen Wert und ist vollständig, wenn Sie es zu diesem Zeitpunkt ausführen ~

Aber wir können setTimeout nicht als Lösung schreiben, wenn wir es verwenden, da wir es kapseln, müssen wir es lösen Das Problem innerhalb der gekapselten Funktion ist, dass wir bei der Ausführung der Auflösungs- und Ablehnungsmethoden auch feststellen können, ob ein Wert vorhanden ist. Wenn nicht, können wir die Ausführung mit setTimeout verzögern folgt~

new MyPromise((resolve, reject) => {
    resolve(1);
}).then(res => {
    console.log(res, 'success');
}, err => {
    console.log(err, 'error');
})
Nach dem Login kopieren

Jetzt versuchen wir

new MyPromise((resolve, reject) => {
    setTimeout(_ => {
        resolve(1);
    }, 0)
}).then(res => {
    console.log(res, 'success');    // 1 "success"
}, err => {
    console.log(err, 'error');
})
Nach dem Login kopieren

Dies löst perfekt das Problem, dass die then-Methode beim ersten Aufruf nicht ausgeführt wird. Gleichzeitig werden verkettete Aufrufe implementiert. Zu den verketteten Aufrufen möchte ich noch ein paar Worte sagen. Unabhängig von den verketteten Aufrufen des Arrays liegt dies daran, dass diese Instanz das letzte Mal zurückgegeben wurde.

Catch-Methode

Catch-Methode dient zum Abfangen von Ausnahmen. Sie ist dasselbe wie die zweite Rückruffunktion der Methode

// 这里是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)
}
Nach dem Login kopieren

resolve Methode

Wir wissen, dass Promsie auch auf diese Weise verwendet werden kann

// 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, '第二次失败');
})
Nach dem Login kopieren
Wir erwarten eine solche Schreibweise, aber jetzt wird definitiv ein Fehler ausgegeben: MyPromise.resolve ist es nicht Eine Methode

Jetzt müssen wir die Auflösungsmethode kapseln. Wir müssen klarstellen, dass Promise den weiteren Aufruf in einer Kette unterstützt. Daher müssen wir die Auflösungsmethode ausführen und a zurückgeben Promise-Instanz

catch(rejectFn) {
    return this.then(null, rejectFn)
}
Nach dem Login kopieren

Reject-Methode

ist wie die Auflösungsmethode, außer dass sie die fehlgeschlagene Funktion

let p1 = MyPromise.resolve(1);
console.log(p1);
Nach dem Login kopieren

Done-Methode<🎜 empfängt >

ES6 Im Standard-Einführungsbuch lautet die Erklärung der done-Methode wie folgt: Unabhängig davon, ob die Rückrufkette des Promise-Objekts mit der then-Methode oder der Catch-Methode endet, solange die letzte Methode auslöst ein Fehler, der möglicherweise nicht abgefangen wird. Zu diesem Zweck stellt Promise eine done-Methode bereit, die immer am Ende der Callback-Kette steht und garantiert alle auftretenden Fehler auslöst. Okay, wir wissen, was diese Methode macht, fangen wir jetzt mit dem Schreiben an ~

static resolve(result) {
    // 返回新的promise实例,执行promise实例中resolve方法
    return new MyPromise(resolve => {
        resolve(result)
    })
}
Nach dem Login kopieren
Sie kann Rückruffunktionen im erfüllten und abgelehnten Zustand empfangen oder keine Parameter bereitstellen. Aber egal was passiert, die done-Methode fängt alle möglichen Fehler ab und wirft sie an die globale

finally-Methode

Die finally-Methode wird unabhängig von Erfolg oder Misserfolg ausgeführt . Methoden, Methoden wie diese und vollständige Methoden in kleinen Programmen usw., lasst uns versuchen, sie zu implementieren

Die all-Methode empfängt ein Array und gibt zurück, wenn jede Instanz im Array erfolgreich ist. Jeder Parameter ist das vom entsprechenden Versprechen zurückgegebene Ergebnis. Wenn ein Element fehlschlägt, gibt die all-Methode einen Fehler zurück >

static reject(reason) {
    // 返回新的promise实例,执行promise实例中reject方法
    return new MyPromise((_, reject) => {
        reject(reason);
    })
}
Nach dem Login kopieren
zur Überprüfung
done(resolveFn, rejectFn) {
    this.then(resolveFn, rejectFn)
        .catch(reason => {
            setTimeout(() => {
                throw reason;
            }, 0)
        })
}
Nach dem Login kopieren

Rennmethode

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;
    })
Nach dem Login kopieren

尝试实现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;);       
    })
Nach dem Login kopieren

推荐教程:《JS教程

Das obige ist der detaillierte Inhalt vonHandgeschriebenes JS zur Implementierung von Promise. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:juejin.im
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage