Heim > Web-Frontend > js-Tutorial > Hauptteil

Detaillierte Schritte zur Implementierung von Promise

不言
Freigeben: 2019-03-29 09:13:57
nach vorne
3249 Leute haben es durchsucht

Der Inhalt dieses Artikels befasst sich mit den detaillierten Schritten zur Umsetzung von Promise. Freunde in Not können sich darauf beziehen.

1. Konstruktor

Zuerst wissen wir alle, dass Promise drei Zustände hat. Der Einfachheit halber definieren wir es als Konstante

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
Nach dem Login kopieren

Als nächstes definieren wir eine Klasse

class MyPromise {
    constructor(executor) {
        //控制状态,使用了一次之后,接下来的都不被使用
        this.state = PENDING;
        this.value = null;
        this.reason = null;
        
        // 定义resolve函数
        const resolve = value => {
            if (this.state === PENDING) {
                this.value = value;
                this.state = FULFILLED;
            }
        }
        
        // 定义reject函数
        const reject = value => {
            if (this.state === PENDING) {
                this.reason = value;
                this.state = REJECTED;
            }
        }
        
        // executor方法可能会抛出异常,需要捕获
        try {
             // 将resolve和reject函数给使用者  
            executor(resolve, reject);
        } catch (error) {
            // 如果在函数中抛出异常则将它注入reject中
            reject(error);
        }
    }
}
Nach dem Login kopieren

Das ist im Grunde leicht zu verstehen

Executor: Dies ist der Parameter, der im Konstruktor beim Instanziieren des Promise-Objekts übergeben wird. Es ist normalerweise eine Funktion (auflösen, ablehnen){. }

Status: „Der Status von Promise, der zu Beginn der Standard-Ausstehende-Status ist, wird bei jedem Aufruf der Auflösungs- und Ablehnungsmethoden geändert. Er wird in der nachfolgenden Then-Methode verwendet.“ .

Wert: Rufen Sie nach erfolgreichem Resolve-Callback den Parameterwert in der Resolve-Methode auf

Grund: Rufen Sie nach erfolgreichem Reject-Callback den Parameterwert in der Reject-Methode auf

resolve: Deklarieren Sie die Auflösungsmethode im Konstruktor und übergeben Sie sie über die eingehende Executor-Methode, um sie an den Benutzer zurückzurufen.

reject: Deklarieren Sie die Reject-Methode im Konstruktor und übergeben Sie sie über den eingehenden Executor Methode zum Zurückrufen an den Benutzer

2. then

then soll das Ergebnis der Auflösung oder Ablehnung in Promise erhalten, dann können wir wissen, dass die then-Methode hier zwei Parameter erfordert, einen Erfolg Rückruf und ein Fehlerrückruf, gehen wir zum Code!

then(onFulfilled, onRejected) {
    if (this.state === FULFILLED) {
        onFulfilled(this.value)
    }
    if (this.state === REJECTED) {
        onRejected(this.reason)
    }
}
Nach dem Login kopieren

Lassen Sie uns einfach den Testcode ausführen

const mp = new MyPromise((resolve, reject)=> {
    resolve('******* i love you *******');
})
mp.then((suc)=> {
console.log(11111, suc);
}, (err)=> {
console.log('****** 你不爱我了*******', err)
})

// 11111 '******* i love you *******'
Nach dem Login kopieren

Es scheint kein Problem zu geben, also versuchen wir es mit der asynchronen Funktion?

const mp = new MyPromise((resolve, reject)=> {
    setTimeout(()=> {
        resolve('******* i love you *******');
    }, 0)
})
mp.then((suc)=> {
console.log(11111, suc);
}, (err)=> {
console.log('****** 你不爱我了*******', err)
})
Nach dem Login kopieren

Wir werden feststellen, dass nichts gedruckt wird. Was ist schief gelaufen? Es stellt sich heraus, dass sich der Wert dieses Zustands aus asynchronen Gründen bei der Ausführung nicht geändert hat, sodass das Urteil ungültig wird. Wie lösen wir es?

Dies wird ein klassischer Rückruf sein. Kommen Sie zum Quellcode

// 存放成功回调的函数
this.onFulfilledCallbacks = [];
// 存放失败回调的函数
this.onRejectedCallbacks = [];

const resolve = value => {
    if (this.state === PENDING) {
        this.value = value;
        this.state = FULFILLED;
        this.onFulfilledCallbacks.map(fn => fn());
    }
}

const reject = value => {
    if (this.state === PENDING) {
        this.value = value;
        this.reason = REJECTED;
        this.onRejectedCallbacks.map(fn => fn());
    }
}
Nach dem Login kopieren

und fügen Sie

then(onFulfilled, onRejected) {
    // ... 
    if(this.state === PENDING) {
        this.onFulfilledCallbacks.push(()=> {
            onFulfilled(this.value);
        });
        this.onRejectedCallbacks.push(()=> {
            onRejected(this.value);
        })
    }
}
Nach dem Login kopieren

hinzu. Nachdem das asynchrone Problem nun gelöst ist, führen wir jetzt den Testcode aus. Die Ergebnisse kamen heraus. Was fehlt uns hier noch?

Kettenaufruf

Was ist zu tun, wenn wir keine Parameter übergeben?

Die Ideen dieser beiden sind auch sehr einfach Geben Sie einfach eine Instanz des Versprechens zurück. Wenn Sie keine Parameter übergeben, handelt es sich um Standardwerte. Werfen wir einen Blick auf den Quellcode

then(onFulfilled, onRejected) {
    let self = this;
    let promise2 = null;
    //解决onFulfilled,onRejected没有传值的问题
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y
    //因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后then的resolve中捕获
    onRejected = typeof onRejected === 'function' ? onRejected : err => {
        throw err;
    }

    promise2 = new MyPromise((resolve, reject) => {
        if (self.state === PENDING) {
            console.log('then PENDING')
            self.onFulfilledCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(self.value);
                    console.log(333333, x, typeof x);
                        self.resolvePromise(promise2, x, resolve, reject);
                    } catch (reason) {
                        reject(reason);
                    }
                }, 0)

            });
            self.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        let x = onRejected(self.reason);
                        self.resolvePromise(promise2, x, resolve, reject);
                    } catch (reason) {
                        reject(reason);
                    }
                }, 0);
            });
        }

        if (self.state === FULFILLED) {
            console.log('then FULFILLED')
            setTimeout(() => {
                try {
                    let x = onFulfilled(self.value);
                    self.resolvePromise(promise2, x, resolve, reject);
                } catch (reason) {
                    reject(reason);
                }
            }, 0);
        }

        if (self.state === REJECTED) {
            console.log('then REJECTED')
            setTimeout(() => {
                try {
                    let x = onRejected(self.reason);
                    self.resolvePromise(promise2, x, resolve, reject);
                } catch (reason) {
                    reject(reason);
                }
            })
        }
    });

    return promise2;
}
Nach dem Login kopieren

Warum müssen wir eine Schicht setTimeout außen einschließen? : Da Promise selbst eine asynchrone Methode ist und zur Mikrotask-Kategorie gehört, muss ihr Wert nach Abschluss des Ausführungsstapels abgerufen werden, sodass alle Rückgabewerte eine asynchrone setTimeout-Ebene enthalten müssen.

Was ist ResolutionPromise? : Dies ist eigentlich die Anforderung des offiziellen Promise/A+. Da Sie dann jede Funktion zurückgeben können, natürlich auch Promise-Objekte, und wenn es sich um ein Promise-Objekt handelt, müssen wir es zerlegen, bis es kein Promise-Objekt mehr ist, und den Wert annehmen.

3. discoverPromise

Schauen wir uns den Code direkt an

resolvePromise(promise2, x, resolve, reject) {
    let self = this;
    let called = false; // called 防止多次调用
    //因为promise2是上一个promise.then后的返回结果,所以如果相同,会导致下面的.then会是同一个promise2,一直都是,没有尽头
    //相当于promise.then之后return了自己,因为then会等待return后的promise,导致自己等待自己,一直处于等待
    if (promise2 === x) {
        return reject(new TypeError('循环引用'));
    }
    //如果x不是null,是对象或者方法
    if (x !== null && (Object.prototype.toString.call(x) === '[object Object]' || Object.prototype.toString.call(x) === '[object Function]')) {
        // x是对象或者函数
        try {
            let then = x.then;

            if (typeof then === 'function') {
                then.call(x, (y) => {
                    // 别人的Promise的then方法可能设置了getter等,使用called防止多次调用then方法
                    if (called) return;
                    called = true;
                    // 成功值y有可能还是promise或者是具有then方法等,再次resolvePromise,直到成功值为基本类型或者非thenable
                    self.resolvePromise(promise2, y, resolve, reject);
                }, (reason) => {
                    if (called) return;
                    called = true;
                    reject(reason);
                });
            } else {
                if (called) return;
                called = true;
                resolve(x);
            }
        } catch (reason) {
            if (called) return;
            called = true;
            reject(reason);
        }
    } else {
        // x是普通值,直接resolve
        resolve(x);
    }
}
Nach dem Login kopieren

Warum müssen wir Promise2 und x am Anfang beurteilen? : Erstens steht in Promise/A+, dass eine Ausnahme ausgelöst werden muss, wenn die beiden gleich sind. Das erste Mal ist p2 Das Ergebnis von then ist ein Promise-Objekt. Als dieses Promise-Objekt erstellt wurde, wurde die Funktion „resolvePromise(promise2,x,reject)“ aufgerufen. Und da x gleich sich selbst ist und ein Promise ist, muss die then-Methode rekursiv ausgeführt werden Es ist kein Promise-Objekt, aber das Ergebnis von x(p2) wartet noch, aber er möchte seine eigene then-Methode ausführen, was zu Wartezeiten führt.

Warum müssen wir die Funktion „resolvePromise“ rekursiv aufrufen? : Ich glaube, vorsichtige Leute haben herausgefunden, dass ich hier die rekursive Aufrufmethode verwende. Zweitens ist dies die Notwendigkeit des Geschäftsszenarios Auch auflösen Wenn ein Promise enthalten ist, muss der Wert rekursiv abgerufen werden, bis x kein Promise-Objekt mehr ist.

4. Catch

//catch方法
catch(onRejected){
  return this.then(null,onRejected)
}
Nach dem Login kopieren

finally

Die Methode „finally“ wird verwendet, um festzulegen, was das Finale ist Der Status des Promise-Objekts lautet: Die Operation wird ausgeführt. Diese Methode ist standardmäßig in ES2018 eingeführt.

finally(fn) {
    return this.then(value => {
        fn();
        return value;
    }, reason => {
        fn();
        throw reason;
    });
};
Nach dem Login kopieren

6. auflösen/ablehnen

Jeder muss die beiden Verwendungen von Promise.resolve() und Promise.reject() gesehen haben Gibt ein Promise-Objekt zurück, implementieren wir es.

static resolve(val) {
    return new MyPromise((resolve, reject) => {
        resolve(val)
    })
}
//reject方法
static reject(val) {
    return new MyPromise((resolve, reject) => {
        reject(val)
    })
}
Nach dem Login kopieren

all

all-Methode ist eine sehr häufig verwendete Methode in Promise. Ihre Funktion besteht darin, ein Array von Promise-Objekten darin zu platzieren. Wann Die then-Methode wird ausgeführt, wenn alles aufgelöst ist, und der Fang wird ausgeführt, wenn eine Ablehnung vorliegt, und ihre Ergebnisse werden auch in der Reihenfolge im Array angeordnet, also implementieren wir sie.

static all(promiseArr) {
    return new MyPromise((resolve, reject) => {
        let result = [];

        promiseArr.forEach((promise, index) => {
            promise.then((value) => {
                result[index] = value;

                if (result.length === promiseArr.length) {
                    resolve(result);
                }
            }, reject);
        });
    });
}
Nach dem Login kopieren

8. Rennen

race方法虽然不常用,但是在Promise方法中也是一个能用得上的方法,它的作用是将一个Promise数组放入race中,哪个先执行完,race就直接执行完,并从then中取值。我们来实现一下吧。

static race(promiseArr) {
    return new MyPromise((resolve, reject) => {
        promiseArr.forEach(promise => {
            promise.then((value) => {
                resolve(value);
            }, reject);
        });
    });
}
Nach dem Login kopieren

9、deferred

static deferred() {
    let dfd = {};
    dfd.promies = new MyPromise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.rfeject = reject;
    });
    return dfd;
};
Nach dem Login kopieren

什么作用呢?看下面代码你就知道了

let fs = require('fs')
let MyPromise = require('./MyPromise')
//Promise上的语法糖,为了防止嵌套,方便调用
//坏处 错误处理不方便
function read(){
  let defer = MyPromise.defer()
  fs.readFile('./1.txt','utf8',(err,data)=>{
    if(err)defer.reject(err)
    defer.resolve(data)
  })
  return defer.Promise
}
Nach dem Login kopieren

10、测试

const mp1 = MyPromise.resolve(1);
const mp2 = MyPromise.resolve(2);
const mp3 = MyPromise.resolve(3);
const mp4 = MyPromise.reject(4);

MyPromise.all([mp1, mp2, mp3]).then(x => {
    console.log(x);
}, (err) => {
    console.log('err1', err);
})
MyPromise.race([mp1, mp4, mp2, mp3]).then(x => {
    console.log(x);
}, (err) => {
    console.log('err2', err);
})

var mp = new MyPromise((resolve, reject) => {
    console.log(11111);
    setTimeout(() => {
        resolve(22222);
        console.log(3333);
    }, 1000);
});
mp.then(x => {
    console.log(x);
}, (err) => {
    console.log('err2', err);
})
//11111
//[ 1, 2, 3 ]
//1
//3333
//22222
Nach dem Login kopieren

完整源码请查看

本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的的JavaScript教程视频栏目!!!

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

Verwandte Etiketten:
Quelle:segmentfault.com
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