Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erklärung von Promise in jQuery, Angular und Node

Detaillierte Erklärung von Promise in jQuery, Angular und Node

php中世界最好的语言
php中世界最好的语言Original
2018-03-16 13:36:401594Durchsuche

Dieses Mal bringe ich Ihnen eine detaillierte Erklärung von Promise in jQuery, Angular und Node. Was sind die Vorsichtsmaßnahmen bei der Verwendung von Promise in jQuery, Angular und Node? sehen.

Ich bin zum ersten Mal auf Promise in jQuery gestoßen. Dieses asynchrone Warteschlangenmodul wird verwendet, um asynchrone Aufgaben und Rückruffunktionen zu entkoppeln. Stellen Sie grundlegende Funktionen für das Ajax-Modul, das Warteschlangenmodul und das Ready-Ereignis bereit. Wenn ich jQuery zum Betreiben von DOM verwende, habe ich keinen starken Wunsch, Promise zu verwenden. In letzter Zeit habe ich Node und Angular gelernt und muss js verwenden, um Geschäftslogik und Datenoperationscode zu schreiben. Im Allgemeinen eignen sich Ereignisse für den Einsatz in interaktiven Szenarien, da das Benutzerverhalten von Natur aus verstreut ist, und Prozesssteuerungähnliche Versprechen eignen sich für die Geschäftsabwicklung in Hintergrundlogik.

    //jQuery1.5之前    $.ajax({
        url: 'url',
        success: function(data, status, xhr) {},
        error: function(xhr, status, msg) {},
        complete: function(xhr, status) {}
    });

Die empfohlene Schreibmethode lautet jetzt:

    $.ajax('url')
        .done(function (data, statusText, xhr) { })
        .done(function (data, statusText, xhr) { })
        .fail(function (data, statusText, error) { })
        .fail(function (data, statusText, error) { })
        .complete(function (xhr, statusText) { })
        .complete(function (xhr, statusText) { });

Warum Promise verwenden?

Was ist der Unterschied? Es scheint, dass ersteres eine Rückruffunktion verwendet und das Letzteres ist eine Kettenformelsyntax und kann wiederholt aufgerufen werden. Die Ausführung von JavaScript erfolgt Single-Threaded und asynchron. Es gibt im Allgemeinen drei Möglichkeiten, mit der asynchronen Programmierung umzugehen: Rückruffunktionen, Ereignisveröffentlichung/-abonnement und Promise/Defferted. Um es ganz klar auszudrücken: Es geht darum, den Prozess der Funktionsausführung besser zu steuern. Die Rückruffunktion ist einfach zu verwenden, aber der Rückruf macht den Aufruf inkonsistent und kann nicht garantiert werden. Wenn Sie sich auf andere Rückrufe verlassen, kann der Codefluss manipuliert werden, was das Debuggen sehr erschwert. Beim Schreiben eines Plug-Ins oder SDKs treten Probleme auf, wenn die Rückruffunktion zu groß ist. Ereignisse sind natürlich viel sauberer als Rückruffunktionen, aber das Ereignisszenario ähnelt einer Benachrichtigung, wenn Methode A ausgeführt wird von A wird sofort ausgelöst. Wenn ein Ereignis eintritt, macht sich der Abonnent sofort nach Erhalt der Benachrichtigung an die Arbeit. Sowohl Ereignisse als auch Rückruffunktionen sind passiv und es gibt keinen Unterschied zwischen dem Ausführungsstatus (Erfolg oder Misserfolg). Entweder müssen Sie mehr Rückrufe schreiben oder sich für mehr Ereignisse anmelden, die daher strukturell von Ihrem Prozess getrennt sein können Rufen Sie eine asynchrone Methode in Ihrem Codeprozess auf (z. B. Controller/Dienst) und erhalten Sie ein bestimmtes Ergebnis. Dann benötigen Sie ein Versprechen. Auch über diesen Namen sollte man nachdenken, denn er scheint „Tu, was du sagst“ zu bedeuten.

Versprechen in jQuery

Die asynchrone Warteschlange von jquery umfasst jQuery.Callbacks(flag), jQuery.Deferred(func) und jQuery.when(). Deferred wird basierend auf Rückrufen implementiert, und jquery.when() basiert auf den ersten beiden. Promise ist eine schreibgeschützte Kopie der asynchronen Warteschlange.

jQuery.Callbacks(flag)

jQuery.Callbacks(flag) gibt ein Kettentoolobjekt zum Verwalten einer Reihe von Rückruffunktionen zurück. Die Rückruffunktionen werden intern über ein Array gespeichert und inspizieren Sie dieses Array. Bietet die Methoden add(), remove(), fireWith/fire()/fired(), disable()/disabled() und lock/locked.

callbacks.add() 用于添加一个或一组回调函数到回调函数列表中
 callbacks.remove()   用于从回调函数列表中移除一个或一组回调函数  
 callbacks.fireWith(context,args)  使用指定的上下文和参数触发回调函数列表中的所有回调函数 
 callbacks.fire() 使用指定的参数触发回调函数中的所有回调函数
 callbacks.fired()  用于判断函数列表是否被触发过
 callbacks.disable()   禁用回调函数列表     
 callbacks.disabled() 用于判断回调函数是否被禁用 
 callbacks.lock() 用于锁定回调函数(锁定memory模式下的回调函数的上下文和参数)
 callback.locked() 用于判断是否被锁定 

 可以跑一下这些示例加深理解:http://www.cnblogs.com/lmule/p/3463515.html 这就不赘述了。

jQuery.Deferred(func)

jQuery.Deferred在jQuery.Callbacks(flags)的基础上为回调函数增加了三种状态:Pending(待定),resolved(成功)和rejected(失败)。它内部维护了三个回调函数列表,分别是成功回调函数列表、失败回调函数列表和消息回调函数列表。调用方法defferred.resolve(args)和resolveWith(context,args)将改变异步队列的状态为成功状态,并会立即执行添加的所有成功回调函数。反之,调用deferred.reject和deferred.rejectWith(context,args)会进入失败状态并触发所有失败的回调函数。一旦进入失败或成功状态,就会保持状态不变。也就是说再次调用deferred.reject()或deferred.resolve()会被忽略.

 tuples =
                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved""reject", "fail", jQuery.Callbacks("once memory"), "rejected""notify", "progress", jQuery.Callbacks("memory"

提供的主要方法如下:

deferred.done(doneCallback[,doneCallback]) 添加成功回调函数,当异步队列处于成功时调用
deferred.fail(failCallback[,failCallback]) 添加失败回调函数,当异步队列处于失败时调用
deferred.process(callback) 添加消息回调函数  
deferred.then(doneCallback,failCallback,[,processCallback]) 同时添加成功、失败、消息回调函数。
deferred.always(callback[,callback]) 添加回调函数,当异步队列处于成功或失败的时候调用
deferred.resolve(args) 使用指定参数调用所有成功函数,队列进入成功状态
deferred.resolveWith(context[,args]) 同上,指定了上下文。
deferred.reject(args) 使用指定参数执行所有失败回调函数,队列进入失败状态
deferred.rejectWith(context[,args]) 同上,指定了上下文。
deferred.notify(args) 调用所有消息回调函数
deferred.notifyWith(context[,args]) 同上,指定了上下文。
deferred.state() 判断异步队列状态  
deferred.promise([taget]) 返回当前deferred对象的只读副本,或者为普通对象增加异步队列的功能

所谓只读副本就是只暴露了添加回调函数和判断方法,done(),fail(),then()等,而不包含触发执行和改变状态的方法:resolve(),rejecet(),notify()等。也就是说,你把事情(回调)交代给promised对象,然后它去做就行了。

jQuery.when(deferreds)

jQuery.when方法提供了一个或多个对象来执行回调函数的功能,通常是具有异步事件的异步队列。也就是说它可以支持同时给多个耗时的操作添加回调函数。

   var doneCallback = function () { console.log('done', arguments); }    var failCallback = function () { console.log('fail', arguments); }
    $.when($.ajax('/Home/Test?id=1'), $.ajax('/Home/Test?id=2')).done(doneCallback).fail(failCallback);

如果都执行成功会进入到done,只要有失败就会进入fail。jQuery.when()都会返回它的只读副本。也就是一个promise。

更多示例可以移步:阮一峰的博客:jQuery的deferred对象详解

ajax中的实现

在Ajax中是将jqXHR对象增加了异步队列的功能,从下面的源码可以看到调用done和success是等价的,同理对于fail和error.

  deferred = jQuery.Deferred(),
  completeDeferred = jQuery.Callbacks("once memory"),
   //..

 // Attach deferreds
        deferred.promise( jqXHR ).complete = completeDeferred.add;
        jqXHR.success = jqXHR.done;
        jqXHR.error = jqXHR.fail;

jqxhr:

 当ajax执行完后,触发回调函数:

        // Success/Error
            if ( isSuccess ) {
                deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
            } else {
                deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
            }            // Status-dependent callbacks            jqXHR.statusCode( statusCode );
            statusCode = undefined;            if ( fireGlobals ) {
                globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
                    [ jqXHR, s, isSuccess ? success : error ] );
            }            // Complete
            completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );

ajax的例子就不用说了,开篇就是。

队列中的实现

队列是一种先进先出的数据结构,jQuery的队列提供了.queue()/jQuery.queue()和dequeue()/jQuery.deueue()实现入队和出队操作,这些是基于数据缓存模块和数组实现。队列提供了一个promise(type,object)方法,返回一个异步队列的只读副本,来观察对应的队列是否执行完成。

        promise: = 1== = =  ( !( -- (  type !== "string"=== type || "fx"( i--= jQuery._data( elements[ i ], type + "queueHooks" ( tmp &&++

运用:

<p id="queue" style=" background-color: wheat;color: green;width: 200px;">queue</p>
  $("#queue").fadeOut(800).delay(1200).fadeIn().animate({ width: 100 }).animate({ height: 100 })
        .promise().done(function() {
            console.log('done!');
        });

默认情况下,animate和fade都进入了队列,所有是按顺序执行的。通过设置queue可以让animate的动画立即执行。

 $("#queue").fadeIn(800).delay(1200).fadeOut().animate({ width: 100 }, { queue: false }).animate({ height: 100 }, { queue: false })
    .promise().done(function() {
            console.log('done!');
        })

如果是多个元素动画:

  <p id="queue" style="display: none;background-color: wheat;color: green;width: 200px;">queue</p>
        <p id="queue1" style="background-color: wheat;color: green;width: 200px;">queue</p>
        <p id="queue2" style="background-color: wheat;color: green;width: 200px;">queue</p>
$("#queue").fadeOut(800,function() {
        console.log("fadeout,do some thing");
        $("#queue1").animate({ width: '100px' }, 2000, function() {
            console.log("queue1,do some thing");
            $('#queue2').animate({
                height: 100
            }, 2000,function() {
                console.log("queue2,do some thing");
            });
        });
    })

一个嵌套一个,有么有感觉像是麻花。可以用promise改造成这样:

   var p1 = $("#queue").fadeOut(800).promise();    var p2 = $("#queue1").animate({ width: 100 }, 2000).promise();    var p3 = $("#queue2").animate({ height: 100 }, 2000).promise();
    $.when(p1).then(p2).then(p3);

是不是很清爽。自定义的正确格式是:

   var async = function (key) {        var deferred = $.Deferred();        function dowork() {            if (key == 1) {
                deferred.resolve("success!");
            } else {
                deferred.reject("fail!");
            }
        }
        setTimeout(dowork, 2000);        return deferred.promise();
    }

就是三步,获取一个deferred,使用resolve/rejcet在内部决定状态,然后返回promise,调用:

async(2).done(function (res) {
        console.log(res);
    }).fail(function (res) {
        console.log(res);
    });

有了上面这些,我们再看去看看Angular和node中的promise。 

Angular中的promise

 promise 是一种用异步方式处理值的方法,promise是对象,代表了一个函数最终可能的返回值或抛出的异常。在与远程对象打交道非常有用,可以把它们看成一个远程对象的代理。

要在Angular中创建promise需要使用内置的$q服务。先用factory定义一个服务,注入$q服务。

angular.module('readApp').factory('asyncService', [    "$q", function ($q) {         var myAsync=function(flag) {            var deferred = $q.defer();            if (flag) {
                deferred.resolve("well done!");
            } else {
                deferred.reject("lost!");
            }             return deferred.promise;
         }        return {
            myAsync: myAsync
        };
    }
]);

获得deferred的方法和jquery不同,但resolve和reject是一样的,最后返回的是promise属性,而不是promise方法。再看如何调用:

angular.module('readApp').controller('testCtrl', ["$scope", "asyncService", function ($scope, asyncService) {
        $scope.flag = true;
        $scope.handle = function () {            asyncService.myAsync($scope.flag).then(function (result) {
                $scope.status = result;                return result;
            }, function (error) {
                $scope.status = error;                return error;
            });
        }
    }])

获取到服务后,调用then方法。then有三个参数,分别对应成功回调、失败回调和通知回调。这个和jquery是一致的。

html:

<p  class="container">
    <label for="flag">成功        <input type="checkbox" id="flag" ng-model="flag" name="name" /> <br />
        <p>{{status}}</p>
        <button ng-click="handle()">点击</button>
    </label></p><footer-n

结果:

不同的是,Angular的promise没有公布jquery那么多方法,我们可以看一下deferred.promise这个属性,它是一个$$state对象。根据Promise/A规范,一个Promise只要具备一个then方法即可。

注意到,Angular中的deferred有notify、reject、resolve三个主要方法和一个promise属性,而这个promise的原型连中包含了我们调用的then方法,then方法在执行完之后会派生一个新的promise,因此可以链式调用。没有done和fail,但是还提供了catch和finally方法。catch就相当于是error方法了。而finally方法就像强类型语言中的场景一样,当我们需要释放一个资源,或者是运行一些清理工作,不管promise是成功还是失败时,这个方法会很有用。要注意的是finally是ie中的一个保留字,需要下面这样调用:

promise[&#39;finally&#39;](function() {});

除了defer()方法,$q还有all和when方法,all(promises)可以将多个promise合并成一个,但如果任意一个promise拒绝了,那么结果的promise也会拒绝。而when(value)方法把一个可能是值或者promise包装成一个$q promise。有了jQuery中的when,这两个方法不难理解。关于这三个方法的示例可以参考这篇博客:AngularJS 中的Promise --- $q服务详解

Angular的$q的灵感是来自[Kris Kowal's Q],从官方的注释中可以看到

 * This is an implementation of promises/deferred objects inspired by
 * [Kris Kowal&#39;s Q](https://github.com/kriskowal/q).

  * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
  * implementations, and the other which resembles ES6 promises to some degree.

支持两种风格,可以像Q库或者jQuery的deferred一样,也可以用ES6语法,文档给出了示例,也是就构造函数法来定义:

 var asyncGreet = function (name) {            return $q(function (resolve, reject) {
                console.log(resolve, reject);
                setTimeout(function () {                    if (name=="stone") {
                        resolve(&#39;Hello, &#39; + name + &#39;!&#39;);
                    } else {
                        reject(&#39;Greeting &#39; + name + &#39; is not allowed.&#39;);
                    }
                }, 1000);
            });
        };

通知(notify/progress)回调还不支持这种写法。对比看,没太大差别。

  function asyncGreet(name) {    var deferred = $q.defer();
    setTimeout(function() {     deferred.notify(&#39;About to greet &#39; + name + &#39;.&#39;);
     if (okToGreet(name)) {        deferred.resolve(&#39;Hello, &#39; + name + &#39;!&#39;);      } else {        deferred.reject(&#39;Greeting &#39; + name + &#39; is not allowed.&#39;);      }     }, 1000);
   return deferred.promise;  }

大致看下源码如何实现:

Promise:

 function Promise() {    this.$$state = { status: 0 };
  }
  extend(Promise.prototype, {
    then: function(onFulfilled, onRejected, progressBack) {      if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {        return this;
      }      var result = new Deferred();      this.$$state.pending = this.$$state.pending || [];      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);      return result.promise;
    },    "catch": function(callback) {      return this.then(null, callback);
    },    "finally": function(callback, progressBack) {      return this.then(function(value) {        return handleCallback(value, true, callback);
      }, function(error) {        return handleCallback(error, false, callback);
      }, progressBack);
    }
  });

创建了一个Promise对象包含一个$$state属性,然后扩展了then,catch,finally方法(注意后两个带了引号)。then的三个参数都是回调函数,对应成功、失败、通知回调,并在then方法中创建了一个deferred作为结果,将回调函数和创建的deferred都存入了数组,主意到这是一个二维数组,每个then对应的promise和回调函数都在这个数组里面。最后返回promise。而catch和finally内部也是调用的then。只要状态大于0也就promise获得了结果就用scheduleProcessQueue处理回调。 Deferred 内部包含了一个promise以及resolve、reject和notify三个方法。jQuery.deferred 中处理的是三个回调队列,Angular中处理的一个是二维数组。 

$http的是一个promise对象:

  var promise = $q.when(config);       //some code       
        promise = promise.then(thenFn, rejectFn);
      }      if (useLegacyPromise) {
        promise.success = function(fn) {
          assertArgFn(fn, 'fn');
          promise.then(function(response) {
            fn(response.data, response.status, response.headers, config);
          });          return promise;
        };
        promise.error = function(fn) {
          assertArgFn(fn, 'fn');
          promise.then(null, function(response) {
            fn(response.data, response.status, response.headers, config);
          });          return promise;
        };
      } else {
        promise.success = $httpMinErrLegacyFn('success');
        promise.error = $httpMinErrLegacyFn('error');
      }      return promise;

用then扩展了error和succes方法,因此我们可以这样使用:

 booksData.getbookById(bookid).success(function(data) {
            vm.book = data;
        }).error(function (e) {
            console.log(e);
            vm.message = "Sorry, something's gone wrong ";
        });

$Interval也是一个promise对象。

Node中的promise

Q

 node中有很多耗时的回调写法,比如写入文件,请求api,查询数据库。node比较早的就是Q库了,也就是angular中参考的版本,先安装Q:

npm install q --save

改写一个写入文件的方法:

var Q = require('q');var writeFile= function() {    var deferred = Q.defer();
    fs.writeFile('public/angular/readApp.min.js', uglified.code, function (err) {        if (err) {
            console.log(err);            deferred.reject(err);
        } else {            deferred.resolve('脚本生产并保存成功: readApp.min.js');  }
    });    return deferred.promise;}
writeFile().then(function(res) {
    console.log(res);
});

风格上和jQuery、angular一模一样。但Q的功能很强大,除了我们现在能想到的Q.all(),Q.when(),还提供了Q.any(),Q.delay(),Q.fcall()等方法。大家可以直接去看文档和源码. 

Q库文档:https://github.com/kriskowal/q

Q库的源码:https://github.com/kriskowal/q/blob/v1/design/q7.js

 bluebird

 这个库是从i5ting的博客看到的,这个库强前后端都能用,而且兼容老ie。使用起来很简单。安装:

npm insatll bluebird --save

比如在前端定义一个promise:

function promptPromise(message) {  return new Promise(function(resolve, reject) {    var result = window.prompt(message);    if (result != null) {
      resolve(result);
    } else {
      reject(new Error('User cancelled'));
    }
  });
}

在后端直接promise整个库: 

var Promise = require('bluebird');var fs = Promise.promisifyAll(require("fs"));
fs.readFileAsync("nodemon.json").then(function(json) {
    console.log("Successful json", json);
}).catch(SyntaxError, function (e) {
    console.error("file contains invalid json");
});

调用promisifyAll方法后,fs对象就拥有了readFileAsync方法。 同理可以用到mongoose对象上:

var User = mongoose.model('User', userSchema);Promise.promisifyAll(User);
 
User.findAsync({ username: "stoneniqiu" }).then(function (data) {
    console.log("success!");
}).catch(function(err) {
    console.log(err);
});

相对于我们之前的不断嵌套的写法这样简洁很多了。

甚至夸张的可以promise所有库:

var ParanoidLib = require("...");var throwAwayInstance = ParanoidLib.createInstance();
Promise.promisifyAll(Object.getPrototypeOf(throwAwayInstance));

但这种做法让人有点担心,有兴趣的可以去官网和git上看看。

Offizielle Website: http://bluebirdjs.com/docs/why-bluebird.html

github: https://github.com/petkaantonov/bluebird

Zusammenfassung: Unterstützt in Knoten Es gibt viele Versprechensbibliotheken, daher werde ich sie nicht einzeln auflisten. Gehen wir zurück zum Thema und leihen uns die Zusammenfassung von i5ting aus:

Vier wichtige Punkte des Versprechens/a+

  • Das Ende der asynchronen Operation Infolgedessen ist jede asynchrone Operation eine weitestgehend unabhängige Operationseinheit

  • Die Hauptmethode zur Interaktion mit Promise ist die Übergabe der Funktion in seine then-Methode (thenable)

  • Ausnahmefehler abfangen

  • Den Prozess basierend auf Ablehnung und Auflösung umgestalten

Nachdem ich diesen Artikel heute horizontal durchgesehen habe, ist es viel klarer geworden. Obwohl es sich bei allen um JavaScript-Codes handelt, handelt es sich in jedem Fall um völlig unterschiedliche Anwendungsszenarien und Implementierungsmethoden Das Versprechen gilt: Je stärker die Kapselung, desto mehr Funktionen hat sie. Anhand dieser Beispiele für JQuery, Angular und Node können wir erkennen, dass Versprechen in der asynchronen Programmierung weit verbreitet sind und es sich lohnt, sie zu lernen. Ich hoffe, dieser Artikel ist hilfreich für Sie. Wenn Sie etwas Unangemessenes finden, können Sie es gerne kommentieren.

Ich glaube, dass Sie die Methode beherrschen, nachdem Sie den Fall in diesem Artikel gelesen haben. Weitere spannende Informationen finden Sie in anderen verwandten Artikeln auf der chinesischen PHP-Website.

Empfohlene Lektüre:

Ausführliche Erklärung der Videowiedergabebibliothek video.js von H5

Ein besonders einfach zu verwendender, leichter Date-Plug -in in JS

Das obige ist der detaillierte Inhalt vonDetaillierte Erklärung von Promise in jQuery, Angular und Node. 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