Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Einführung in den Beispielcode des Promise-Musters der asynchronen JavaScript-Programmierung

Detaillierte Einführung in den Beispielcode des Promise-Musters der asynchronen JavaScript-Programmierung

黄舟
黄舟Original
2017-03-11 15:19:571246Durchsuche

Der asynchrone Modus wird in der Webprogrammierung immer wichtiger. Aus diesem Grund ist dieser Modus nicht sehr einfach zu implementieren Es handelt sich um eine Abstraktion des Versprechens (manchmal auch als aufgeschoben bezeichnet). Über diese Bibliotheken können Entwickler das Promise-Muster in der tatsächlichen Programmierung verwenden. Im offiziellen IE-Blog wurde kürzlich ein Artikel veröffentlicht, in dem detailliert beschrieben wird, wie XMLHttpRequest2 zum Üben des Versprechenmodus verwendet wird. Werfen wir einen Blick auf die zugehörigen Konzepte und Anwendungen.

Betrachten Sie ein Beispiel, bei dem eine Webseite über asynchrone Vorgänge verfügt (über XMLHttpRequest2 oder Web Worker). Mit der Vertiefung der Web 2.0-Technologie wird der Rechendruck auf der Browserseite immer größer, sodass „Parallelität“ eine positive Bedeutung hat. Für Entwickler müssen sie nicht nur die Interaktion zwischen der Seite und dem Benutzer unberührt lassen, sondern auch die Beziehung zwischen der Seite und asynchronen Aufgaben koordinieren. Diese Art von nichtlinearen Ausführungsprogrammieranforderungen stellt Schwierigkeiten bei der Anpassung dar. Abgesehen von der Seiteninteraktion können wir uns zwei Ergebnisse vorstellen, die für asynchrone Aufrufe verarbeitet werden müssen: erfolgreiche Vorgänge und Fehlerverarbeitung. Nach einem erfolgreichen Aufruf müssen wir möglicherweise das zurückgegebene Ergebnis in einer anderen Ajax-Anfrage verwenden, was zu einer „Funktionsketten“-Situation führt (ausführlich in der Erklärung des Autors zum anderen Artikel „Asynchroner Programmierstil von NodeJS“). Diese Situation führt zu Programmierkomplikationen. Schauen Sie sich das folgende Codebeispiel an (basierend auf XMLHttpRequest2):

function searchTwitter(term, onload, onerror) {

     var xhr, results, url;
     url = 'http://search.twitter.com/search.json?rpp=100&q=' + term;
     xhr = new XMLHttpRequest();
     xhr.open('GET', url, true);

     xhr.onload = function (e) {
         if (this.status === 200) {
             results = JSON.parse(this.responseText);
             onload(results);
         }
     };

     xhr.onerror = function (e) {
         onerror(e);
     };

     xhr.send();
 }

 function handleError(error) {
     /* handle the error */
 }

 function concatResults() {
     /* order tweets by date */
 }

 function loadTweets() {
     var container = document.getElementById('container');

     searchTwitter('#IE10', function (data1) {
         searchTwitter('#IE9', function (data2) {
             /* Reshuffle due to date */
             var totalResults = concatResults(data1.results, data2.results);
             totalResults.forEach(function (tweet) {
                 var el = document.createElement('li');
                 el.innerText = tweet.text;
                 container.appendChild(el);
             });
         }, handleError);
     }, handleError);
 }

Die Funktion des obigen Codes besteht darin, den Inhalt mit den Hashtags IE10 und IE9 in Twitter abzurufen und auf der Seite anzuzeigen. Diese Art von verschachtelter Rückruffunktion ist schwer zu verstehen. Entwickler müssen sorgfältig analysieren, welcher Code für die Geschäftslogik der Anwendung verwendet wird und welcher Code asynchrone Funktionsaufrufe verarbeitet. Die Fehlerbehandlung ist ebenfalls zerlegt. Wir müssen das Auftreten von Fehlern an verschiedenen Stellen erkennen und entsprechend behandeln.

Um die Komplexität der asynchronen Programmierung zu reduzieren, haben Entwickler nach einfachen Möglichkeiten zur Handhabung asynchroner Vorgänge gesucht. Eines dieser Verarbeitungsmuster wird als Versprechen bezeichnet und stellt das Ergebnis eines Vorgangs dar, der möglicherweise lange dauert und nicht unbedingt abgeschlossen sein muss. Anstatt zu blockieren und auf den Abschluss eines langen Vorgangs zu warten, gibt dieses Muster ein Objekt zurück, das das versprochene Ergebnis darstellt.

Betrachten Sie ein Beispiel, bei dem der Seitencode auf eine Drittanbieter-API zugreifen muss. Netzwerkverzögerungen können zu längeren Antwortzeiten führen. In diesem Fall hat die Verwendung der asynchronen Programmierung keinen Einfluss auf die Interaktion zwischen der gesamten Seite und dem Benutzer. Der Promise-Modus implementiert normalerweise eine Methode namens then, um die entsprechende Rückruffunktion zu registrieren, wenn sich der Status ändert. Zum Beispiel das folgende Codebeispiel:

searchTwitter(term).then(filterResults).then(displayResults);

Der Versprechensmodus befindet sich jederzeit in einem der folgenden drei Zustände: unerfüllt, gelöst und abgelehnt. Am Beispiel des CommonJS Promise/A-Standards ist die then-Methode des Promise-Objekts für das Hinzufügen von Verarbeitungsfunktionen für die abgeschlossenen und abgelehnten Zustände verantwortlich. Die Methode then gibt ein weiteres Promise-Objekt zurück, um die Bildung einer Promise-Pipeline zu erleichtern. Diese Methode zur Rückgabe von Promise-Objekten kann Entwickler bei der Verkettung asynchroner Vorgänge unterstützen, z. B. then(resolvedHandler, RejectHandler);. Die Callback-Funktion „resolvedHandler“ wird ausgelöst, wenn das Promise-Objekt in den Abschlussstatus eintritt und das Ergebnis liefert. Die Funktion „rejectedHandler“ wird im Ablehnungsstatus aufgerufen.

Mit dem Promise-Muster können wir das obige Twitter-Beispiel erneut implementieren. Um die Implementierungsmethode besser zu verstehen, haben wir versucht, ein Versprechensmuster-Framework von Grund auf zu erstellen. Zunächst benötigen Sie einige Objekte zum Speichern von Versprechen.

var Promise = function () {
        /* initialize promise */
    };

Als nächstes definieren Sie die Methode then, die zwei Parameter für die Behandlung des Abschluss- und Ablehnungsstatus akzeptiert.

Promise.prototype.then = function (onResolved, onRejected) {
     /* invoke handlers based upon state transition */
 };

Gleichzeitig werden zwei Methoden benötigt, um den Statusübergang von unvollständig nach abgeschlossen und von unvollständig nach abgelehnt durchzuführen.

Promise.prototype.resolve = function (value) {
     /* move from unfulfilled to resolved */
 };

 Promise.prototype.reject = function (error) {
     /* move from unfulfilled to rejected */
 };

Da nun ein Versprechensregal erstellt wurde, können wir das obige Beispiel fortsetzen, vorausgesetzt, wir erhalten nur den Inhalt von IE10. Erstellen Sie eine Methode zum Senden der Ajax-Anfrage und verpacken Sie sie in ein Versprechen. Dieses Promise-Objekt gibt den Übergangsprozess des Abschluss- und Ablehnungsstatus in xhr.onload bzw. xhr.onerror an. Bitte beachten Sie, dass die searchTwitter-Funktion das Promise-Objekt zurückgibt. Verwenden Sie dann in LoadTweets die Methode then, um die Rückruffunktionen festzulegen, die dem Abschluss- und Ablehnungsstatus entsprechen.

function searchTwitter(term) {

    var url, xhr, results, promise;
    url = 'http://search.twitter.com/search.json?rpp=100&q=' + term;
    promise = new Promise();
    xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);

    xhr.onload = function (e) {
        if (this.status === 200) {
            results = JSON.parse(this.responseText);
            promise.resolve(results);
        }
    };

    xhr.onerror = function (e) {
        promise.reject(e);
    };

    xhr.send();
    return promise;
}

function loadTweets() {
    var container = document.getElementById('container');
    searchTwitter('#IE10').then(function (data) {
        data.results.forEach(function (tweet) {
            var el = document.createElement('li');
            el.innerText = tweet.text;
            container.appendChild(el);
        });
    }, handleError);
}

Bisher können wir den Promise-Modus auf eine einzelne Ajax-Anfrage anwenden, aber es scheint, dass die Vorteile des Promises nicht zum Ausdruck kommen. Werfen wir einen Blick auf die gleichzeitige Zusammenarbeit mehrerer Ajax-Anfragen. An diesem Punkt benötigen wir eine andere Methode, um das Promise-Objekt aufzurufen zu speichern. Sobald ein Versprechen vom unvollständigen Zustand in den abgeschlossenen oder abgelehnten Zustand umgewandelt wird, wird die entsprechende Handlerfunktion in der then-Methode aufgerufen. Die when-Methode ist wichtig, wenn Sie warten müssen, bis alle Vorgänge abgeschlossen sind.

Promise.when = function () {
    /* handle promises arguments and queue each */
};

Nehmen Sie als Beispiel das Szenario, in dem wir gerade den Inhalt von IE10 und IE9 erhalten haben. Wir können den Code wie folgt schreiben:

var container, promise1, promise2;
container = document.getElementById('container');
promise1 = searchTwitter('#IE10');
promise2 = searchTwitter('#IE9');
Promise.when(promise1, promise2).then(function (data1, data2) {

    /* Reshuffle due to date */
    var totalResults = concatResults(data1.results, data2.results);
    totalResults.forEach(function (tweet) {
        var el = document.createElement('li');
        el.innerText = tweet.text;
        container.appendChild(el);
    });
}, handleError);

分析上面的代码可知,when函数会等待两个promise对象的状态发生变化再做具体的处理。在实际的Promise库中,when函数有很多变种,比如 when.some()、when.all()、when.any()等,读者从函数名字中大概能猜出几分意思来,详细的说明可以参考CommonJS的一个promise实现when.js。

除了CommonJS,其他主流的Javascript框架如jQuery、Dojo等都存在自己的promise实现。开发人员应该好好利用这种模式来降低异步编程的复杂性。我们选取Dojo为例,看一看它的实现有什么异同。

Dojo框架里实现promise模式的对象是Deferred,该对象也有then函数用于处理完成和拒绝状态并支持串联,同时还有resolve和reject,功能如之前所述。下面的代码完成了Twitter的场景:

function searchTwitter(term) {

    var url, xhr, results, def;
    url = 'http://search.twitter.com/search.json?rpp=100&q=' + term;
    def = new dojo.Deferred();
    xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);

    xhr.onload = function (e) {
        if (this.status === 200) {
            results = JSON.parse(this.responseText);
            def.resolve(results);
        }
    };

    xhr.onerror = function (e) {
        def.reject(e);
    };

    xhr.send();
    return def;
}

dojo.ready(function () {
    var container = dojo.byId('container');
    searchTwitter('#IE10').then(function (data) {
        data.results.forEach(function (tweet) {
            dojo.create('li', {
                innerHTML: tweet.text
            }, container);
        });
    });
});

不仅如此,类似dojo.xhrGet方法返回的即是dojo.Deferred对象,所以无须自己包装promise模式。

var deferred = dojo.xhrGet({
    url: "search.json",
    handleAs: "json"
});

deferred.then(function (data) {
    /* handle results */
}, function (error) {
    /* handle error */
});

除此之外,Dojo还引入了dojo.DeferredList,支持开发人员同时处理多个dojo.Deferred对象,这其实就是上面所提到的when方法的另一种表现形式。

dojo.require("dojo.DeferredList");
dojo.ready(function () {
    var container, def1, def2, defs;
    container = dojo.byId('container');
    def1 = searchTwitter('#IE10');
    def2 = searchTwitter('#IE9');

    defs = new dojo.DeferredList([def1, def2]);

    defs.then(function (data) {
        // Handle exceptions
        if (!results[0][0] || !results[1][0]) {
            dojo.create("li", {
                innerHTML: 'an error occurred'
            }, container);
            return;
        }
        var totalResults = concatResults(data[0][1].results, data[1][1].results);

        totalResults.forEach(function (tweet) {
            dojo.create("li", {
                innerHTML: tweet.text
            }, container);
        });
    });
});

上面的代码比较清楚,不再详述。

说到这里,读者可能已经对promise模式有了一个比较完整的了解,异步编程会变得越来越重要,在这种情况下,我们需要找到办法来降低复杂度,promise模式就是一个很好的例子,它的风格比较人性化,而且主流的JS框架提供了自己的实现。所以在编程实践中,开发人员应该尝试这种便捷的编程技巧。需要注意的是,promise模式的使用需要恰当地设置promise对象,在对应的事件中调用状态转换函数,并且在最后返回promise对象。

技术社区对异步编程的关注也在升温,国内社区也发出了自己的声音。资深技术专家老赵就发布了一套开源的异步开发辅助库Jscex,它的设计很巧妙,抛弃了回调函数的编程方式,采用一种“线性编码、异步执行”的思想,感兴趣的读者可以查看这里。

不仅仅是前端的JS库,如今火热的NodeJS平台也出现了许多第三方的promise模块,具体的清单可以访问这里。

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in den Beispielcode des Promise-Musters der asynchronen JavaScript-Programmierung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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