Heim  >  Artikel  >  Web-Frontend  >  Eine ausführliche Einführung in Promises in JavaScript

Eine ausführliche Einführung in Promises in JavaScript

黄舟
黄舟Original
2017-03-04 15:30:151397Durchsuche

1. Einführung

JavaScript ist Single-Threaded und kann jeweils nur eine Aufgabe ausführen. Wenn eine Aufgabe lange dauert, müssen nachfolgende Aufgaben warten. Gibt es also eine Möglichkeit, ein solches Problem zu lösen? (Lassen Sie WebWorker beiseite), das heißt, den Code asynchron ausführen zu lassen. Was bedeutet das? Wenn Sie beispielsweise eine asynchrone Ajax-Anfrage stellen, wird der Wert von readyState kontinuierlich überwacht, um die Ausführung der angegebenen Rückruffunktion zu bestimmen.

Es gibt normalerweise drei Arten der asynchronen Ausführung: Rückruffunktionen, Ereignisüberwachung sowie Veröffentlichung und Abonnement. Ereignisüberwachung und Veröffentlichung und Abonnement sind tatsächlich ähnlich, letzteres ist jedoch robuster.

Wie die Rückruffunktion ist die Rückruffunktion die einfachste Programmieridee, die bei der asynchronen Ausführung angewendet wird. Wie folgt:

function async(item,callback){
    console.log(item);
    setTimeout(function(){
        callback(item+1);
    },1000);    
}

Wenn im obigen Beispiel die asynchrone Funktion ausgeführt wird, ist der Druckvorgang abgeschlossen und die Rückruffunktion wird nach 1 Sekunde (aber nicht unbedingt 1 Sekunde) ausgeführt. Einzelheiten finden Sie unter "setTimeout Dinge").

Der Hauptzweck von Asynchronität besteht darin, Nichtblockierung zu handhaben und die Leistung zu verbessern. Stellen Sie sich vor, eine Operation erfordert mehrere asynchrone Funktionsoperationen wie folgt:

async(1, function(item){
    async(item, function(item){
        async(item, function(item){
            console.log('To be continued..');
        });
    });
});

Ist das nicht etwas schwierig zu lesen?

Als weiteres Beispiel können wir die Ausnahmeerfassung hinzufügen, um den obigen Code robuster zu machen. Im asynchronen Modus ist die Ausnahmebehandlung auf verschiedene Rückruffunktionen verteilt. Wir können Ausnahmen beim Aufruf nicht über try...catch behandeln, daher ist es schwierig, dies effektiv und klar zu tun.

Hey, was soll ich tun?

Boom boom boom, boom boom boom – Promise feiert sein Debüt.

Wenn Sie ES6 Promise verwenden, um den obigen Code zu optimieren, erhalten Sie Folgendes:

function opration(item){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve(item+1);
        },1000);
    });
    console.log(item);
    return p;
}
function failed(e){
    console.log(e);
}
Promise.resolve(1).then(opration).then(opration).then(opration).catch(failed);

Wenn Sie Promise verwenden, um den Code zu optimieren, liegt der Vorteil auf der Hand, wenn Sie das ändern Rückruffunktion in eine Kette einbinden Die Rückrufmethode vermeidet Verschachtelungsebenen, wodurch der Programmablauf klar und deutlich wird und von einer oder mehreren Rückruffunktionen ausgelöste Fehler durch die Catch-Methode einheitlich behandelt werden.

Ups, das ist gut, aber wer ist das Versprechen in ES6 und was sind die spezifischen Nutzungsregeln? Lassen Sie es uns gemeinsam erkunden.

2. Übersicht über Promise

Promise ist eine Lösung für die asynchrone Programmierung, die sinnvoller ist als herkömmliche Lösungen (Rückrufe und Ereignisse) und mächtiger. Es wurde zuerst von der Community vorgeschlagen und implementiert. ES6 hat es in den Sprachstandard geschrieben, seine Verwendung vereinheitlicht und Promise-Objekte nativ bereitgestellt. Das Objekt

Promise hat nur drei Zustände:

1, ausstehend: Der asynchrone Vorgang ist nicht abgeschlossen.

2. gelöst: Der asynchrone Vorgang wurde abgeschlossen.

3. abgelehnt: Der asynchrone Vorgang ist fehlgeschlagen.

Darüber hinaus gibt es in diesen drei Zuständen nur zwei Änderungsmodi, und sobald sich der Zustand ändert, ändert er sich nicht mehr:

1. Asynchroner Vorgang aus „Ausstehend“. bis gelöst ;

2. Asynchroner Vorgang von ausstehend zu abgelehnt

Okay, da es zur ES6-Spezifikation gehört, können wir das Versprechen direkt über Chrome ausdrucken Schauen Sie sich dieses Ding an:

Nun, Es ist auf den ersten Blick klar, dass Promise ein Konstruktor ist, also können wir dadurch instanziieren unser eigenes Promise-Objekt und nutzen es.

3. Leitfaden „Erste Schritte mit Promise“

Da Promise ein Konstruktor ist, werfen wir zunächst einen Blick auf den neuen. Wie folgt:

var p = new Promise();

und führen Sie den obigen Code aus. Der Chrome-Screenshot sieht wie folgt aus:

Warum wird der Fehler gemeldet?

Oh, der Promise-Konstruktor benötigt übrigens eine Funktion als Parameter, und die Funktion als Parameter hat die Funktion, wenn der asynchrone Vorgang von „Ausstehend“ in „Aufgelöst“ wechselt Es wird aufgerufen, wenn der zweite Parameter aufgerufen wird, wenn der asynchrone Vorgang von „Ausstehend“ in „Abgelehnt“ wechselt.

Zum Beispiel habe ich den ersten Parameter der anonymen Funktion „resolve“ genannt; der zweite Parameter wurde „reject“ genannt. Die Demo lautet wie folgt:

var p = new Promise(function(resolve, reject){
    console.log('new一个Promise对象');
    setTimeout(function(){
        resolve('Monkey');
    },1000);
});

und führen Sie den obigen Code aus. Der Chrome-Screenshot lautet wie folgt:

Besondere Erinnerung: Beim Übergeben einer anonymen Funktion Als Parameter des Konstruktors Promise wurde die anonyme Funktion bereits ausgeführt, als wir eine neue Funktion erstellten, wie in der Abbildung oben gezeigt.

Hey, im obigen Code verwenden wir den setTimeout-Timer in der anonymen Funktion und rufen „resolve“ nach 1 Sekunde auf. Warum wird kein Undefinierter oder Fehler gemeldet? !

Das ist die Kraft des Versprechens. Aus diesem Grund können wir asynchrone Vorgänge in elegante Kettenaufrufe umschreiben. Wie nennt man es?

Erinnern Sie sich, dass wir im Abschnitt „Promise-Übersicht“ Promise durch Chrom gedruckt und den Bereich im roten Linienrahmen verwendet haben? Darunter gibt es eine then-Methode (Promise.prototype.then) im Promise-Prototyp. Durch diese then-Methode reicht es aus. Wie folgt:

p.then(function(value){
    console.log(value);
});

Unter diesen hat die then-Methode zwei anonyme Funktionen als Parameter, die den Auflösungs- und Ablehnungsparametern von Promise entsprechen. Führen Sie den Code aus und die Ergebnisse sind wie folgt:

好了,当then执行完后,如果我们想继续在其之后看,使用then方法链式调用,有两种情况,一种是直接返回非Promise对象的结果;另一种是返回Promise对象的结果。

1、返回非Promise对象的结果:紧跟着的then方法,resolve立刻执行。并可使用前一个then方法返回的结果。如下:

p.then(function(value){
    console.log(value);
    //返回非Promise对象,如我的对象
    return {
        name: 'Dorie',
        age: 18
    };
}).then(function(obj){
    console.log(obj.name);
});

执行上述完整代码,chrome截图如下:

2、返回Promise对象的结果:紧跟着的then方法,与new Promise后的then方法一样,需等待前面的异步执行完后,resolve方可被执行。如下:

p.then(function(value){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            var message = value + ' V Dorie'
            resolve(message);
        },1000);
    });
    console.log(value);
    //返回一个Promise对象
    return p;
}).then(function(value){
    console.log(value);
});

执行上述完整代码,chrome截图如下:

那么,当创建、执行Promise方法中有异常报错,如何捕获呢?

Promise.prototype.catch原型方法,就是为其而设定的。它具有冒泡的特性,比如当创建Promise实例时,就出错了,错误消息就会通过链式调用的这条链,一直追溯到catch方法,如果找到尽头都没有,就报错,并且再找到catch之前的所有then方法都不能执行了。Demo如下(代码太长,请自行展开):

<!DOCTYPE html>
    <head>
        <title>test</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            var p = new Promise(function(resolve, reject){
                //M未定义
                console.log(M);
                setTimeout(function(){
                    resolve(&#39;Monkey&#39;);
                },1000);
            });
            p.then(function(value){
                var p = new Promise(function(resolve, reject){
                    setTimeout(function(){
                        var message = value + &#39; V Dorie&#39;
                        resolve(message);
                    },1000);
                });
                console.log(value);
                //返回一个Promise对象
                return p;
            }).then(function(value){
                console.log(value);
                return &#39;next is catch&#39;;
            }).catch(function(e){
                console.log(e);
            }).then(function(value){
                console.log(&#39;execute,but value is &#39; + value);
            });
        </script>
    </body>
</html>

执行上述代码,chrome截图如下:

好了,到这里,我们已经了解了最常用的Promise.prototype.then和Promise.prototype.catch这两个原型方法。另外,像Promise构造函数还有属于自身的方法,如all、rece、resolve、reject等,详情请点击这里(here)。

通过一路上对Promise的讲述,我们也有了一定的认识,其实Promise并没有想象中的那么难以理解嘛。懂得Promise概念后,其实我们自己也可以实现一个简易版的Promise。下面就一同尝试实现一个呗。

四、模拟Promise

假设:有三个异步操作方法f1,f2,f3,且f2依赖于f1,f3依赖于f2。如果,我们采用ES6中Promise链式调用的思想,我们可以将程序编写成这样:

f1().then(f2).then(f3);

那么,通过上面这一系列链式调用,怎样才能达到与ES6中Promise相似的功能呢?

初步想法:首先将上述链式调用的f2、f3保存到f1中,当f1中的异步执行完后,再调用执行f2,并将f1中的f3保存到f2中,最后,等f2中的异步执行完毕后,调用执行f3。详细构思图,如下:

从上图可知,由于f1、f2 、f3是可变得,所以存储数组队列thens,可放入,我们即将创建的模拟Promise构造函数中。具体实现代码如下:

//模拟Promise
function Promise(){
    this.thens = [];
};
Promise.prototype = {
    constructor: Promise,
    then: function(callback){
        this.thens.push(callback);
        return this;        
    }
};

并且,需要一个Promise.prototype.resolve原型方法,来实现:当f1异步执行完后,执行紧接着f1后then中的f2方法,并将后续then中方法,嫁接到f2中,如f3。具体实现代码如下:

//模拟Promise,增加resolve原型方法
function Promise(){
    this.thens = [];
};
Promise.prototype = {
    constructor: Promise,
    then: function(callback){
        this.thens.push(callback);
        return this;        
    },
    resolve: function(){
        var t = this.thens.shift(), 
            p;
        if(t){
            p = t.apply(null,arguments);
            if(p instanceof Promise){
                p.thens = this.thens;
            }
        }
    }
};

测试代码(代码太长,自行打开并运行)。

function f1() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(1);
        promise.resolve();
    }, 1500)

    return promise;
}

function f2() {
    var promise = new Promise();
    setTimeout(function () {
        console.log(2);
        promise.resolve();
    }, 1500);
    return promise;
}

function f3() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(3);
        promise.resolve();
    }, 1500)

    return promise;
}
f1().then(f2).then(f3);

仔细品味,上述实现的Promise.prototype.resolve方法还不够完美,因为它只能够满足于f1、f2、f3等方法都是使用模拟的Promise异步执行的情况。而,当其中有不是返回的Promise对象的呢,而是返回一个数字,亦或是什么也不返回,该怎么办?所以,针对以上提出的种种可能,再次改进resolve。改善代码如下:

//模拟Promise,改善resolve原型方法
var Promise = function () {
    this.thens = [];
};
Promise.prototype = {
    constructor: Promise,
    then: function(callback){
        this.thens.push(callback);
        return this;        
    },
    resolve: function () {
        var t,p;
        t = this.thens.shift();
        t && (p = t.apply(null, arguments));
        while(t && !(p instanceof Promise)){
            t = this.thens.shift();
            t && (p = t.call(null, p));    
        }
        if(this.thens.length){
            p.thens = this.thens;
        };
    }
}

测试代码(代码太长,自行打开并运行)。

function f1() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(1);
        promise.resolve();
    }, 1500)

    return promise;
}

function f2() {
    var promise = new Promise();
    setTimeout(function () {
        console.log(2);
        promise.resolve();
    }, 1500);
    return promise;
}

function f3() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(3);
        promise.resolve();
    }, 1500)

    return promise;
}

function f4() {
    console.log(4);
    return 11;
}

function f5(x) {
    console.log(x+1);
}

function f6() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(6);
        promise.resolve();
    }, 1500)

    return promise;
}

function f7() {
    console.log(7);
}

var that = f1().then(f2).then(f3).then(f4).then(f5).then(f6).then(f7);

好了,初步模拟的Promise就OK啦。

吼吼,对于Promise,我们这一路走来,发现原来也不过如此呢。

 以上就是详细介绍JavaScript 中的 Promise的内容,更多相关内容请关注PHP中文网(www.php.cn)!

Stellungnahme:
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