>웹 프론트엔드 >JS 튜토리얼 >jQuery, Angular 및 노드의 Promise에 대한 자세한 설명

jQuery, Angular 및 노드의 Promise에 대한 자세한 설명

php中世界最好的语言
php中世界最好的语言원래의
2018-03-16 13:36:401664검색

이번에는 jQuery, Angular, node에서 Promise에 대한 자세한 설명을 가져왔습니다. jQuery, Angular, node에서 Promise를 사용할 때의 주의사항은 무엇인가요?

jQuery에서 Promise를 처음 접했습니다. Deferred Object는 jQuery 1.5에서 도입되었습니다. 이 비동기 대기열 모듈은 비동기 작업과 콜백 함수를 분리하는 데 사용됩니다. ajax 모듈, 큐 모듈, 준비 이벤트에 대한 기본 기능을 제공합니다. jQuery를 사용하여 DOM을 운영할 때 Promise를 사용하려는 의욕이 별로 없습니다. 최근에는 node와 Angular를 배우면서 비즈니스 로직과 데이터 작업 코드를 작성하려면 js를 사용해야 합니다. 일반적으로 이벤트는 대화형 시나리오에 사용하기에 적합합니다. 사용자 행동은 본질적으로 분산되어 있고 프로세스 제어약속과 같은는 백그라운드 로직

에서 비즈니스를 처리하는 데 적합하기 때문입니다.

    //jQuery1.5之前    $.ajax({
        url: 'url',
        success: function(data, status, xhr) {},
        error: function(xhr, status, msg) {},
        complete: function(xhr, status) {}
    });
현재 권장하는 작성 방법은

    $.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) { });

Promise를 사용하는 이유

차이점은 전자는 콜백 함수를 사용하는 것 같은데, 후자는 체인 구문이라 반복 호출이 가능한 것 같습니다. JavaScript의 실행은 단일 스레드 및 비동기식입니다. 일반적으로 비동기 프로그래밍을 처리하는 방법에는 콜백 함수, 이벤트 게시/구독 및 Promise/Defferred가 있습니다. 직설적으로 말하면 함수 실행 과정을 더 효과적으로 제어하기 위한

것입니다. 콜백 함수는 사용이 간편하지만 콜백으로 인해 호출이 일관성이 없게 되고 다른 콜백에 의존할 경우 코드 흐름이 변조될 수 있어 디버깅이 매우 어려워집니다. 플러그인이나 SDK를 작성할 때 이벤트에 비해 콜백 함수가 너무 크면 문제가 발생하는데, 당연히 콜백 함수보다 훨씬 깔끔하지만 이벤트 시나리오는 메소드 A가 실행될 때 실행되는 것과 비슷합니다. A의 경우 이벤트가 발생하면 알림을 받은 후 즉시 작업에 들어갑니다. 이벤트와 콜백 함수는 모두 수동적이며 실행 상태(성공 또는 실패) 간에 구분이 없습니다. 따라서 더 많은 콜백을 작성하거나 더 많은 이벤트를 구독해야 하며, 따라서 원하는 경우 이는 프로세스와 구조적으로 분리될 수 있습니다. 코드 프로세스(예: 컨트롤러/서비스)에서 비동기 메서드를 호출하고 특정 결과를 얻으면 약속이 필요합니다. 이 이름 역시 '말한 대로 하라'는 뜻인 것 같아 생각해 볼 가치가 있다.

jQuery의 약속

jquery의 비동기 대기열에는 jQuery.Callbacks(flag), jQuery.Deferred(func) 및 jQuery.when()이 포함됩니다. Deferred는 콜백을 기반으로 구현되고 jquery.when()은 처음 두 개를 기반으로 합니다. Promise는 비동기 대기열의 읽기 전용 복사본입니다

.

jQuery.Callbacks(flag)

jQuery.Callbacks(flag)는 콜백 함수 집합을 관리하기 위한 체인 도구 개체를 반환합니다. 콜백 함수는 배열을 통해 내부적으로 저장되며, 이 배열을 중심으로 다른 메서드가 작동하고 감지됩니다. add(), 제거(), fireWith/fire()/fired(), 비활성화()/disabled(), 잠금/잠금 메서드를 제공합니다. callbacks.add() 하나 또는 콜백 함수 그룹을 콜백 함수 목록에 추가하는 데 사용됩니다. callbacks.remove()  하나 또는 콜백 함수 그룹을 제거하는 데 사용됩니다. 콜백 함수 목록에서 콜백 함수 callbacks.fireWith(context,args) 지정된 컨텍스트와 매개변수를 사용하여 콜백 함수 목록의 모든 콜백 함수를 트리거합니다. callbacks.fire() 사용 콜백 함수를 트리거하기 위해 지정된 매개변수 모든 콜백 함수 callbacks.fired()는 함수 목록이 트리거되었는지 확인하는 데 사용됩니다 callbacks.disable() 비활성화된 콜백 함수 목록 callbacks.disabled() 콜백 함수가 비활성화되었는지 확인하는 데 사용됩니다. callbacks.lock() 콜백 함수를 잠그는 데 사용됩니다(메모리 모드에서 콜백 함수의 컨텍스트 및 매개변수를 잠급니다) 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上看看。

공식 웹사이트: http://bluebirdjs.com/docs/why-bluebird.html

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

요약: Node에서 Promise를 지원하는 라이브러리가 많기 때문에 여기에 있습니다. 주제로 돌아가서 i5ting의 요약을 빌리겠습니다.

약속의 네 가지 핵심 사항/a+

  • 비동기 작업의 최종 결과는 가능한 한 각 비동기 작업이 독립적인 작업 단위입니다.

  • Promise에서 가장 중요한 것은 대화형 방식에서 함수를 then 메소드에 전달하는 것입니다(thenable)

  • 예외 포착 오류 포착

  • 거부 및 해결을 기반으로 프로세스 재구성

I 예전에는 Promise에 대해 막연한 느낌이 있었는데 오늘은 수평적으로 정리해 봤습니다. 모두 JavaScript 코드이지만 전혀 다른 응용 시나리오와 구현 방법이지만 Promise의 본질은 다음과 같습니다. 캡슐화가 강할수록 더 많은 기능을 갖습니다. jquery, Angular 및 node의 이러한 예를 통해 Promise가 비동기 프로그래밍에서 널리 사용되며 학습할 가치가 있음을 알 수 있습니다. 이 글이 도움이 되었으면 좋겠습니다. 부적절한 내용이 있으면 언제든지 댓글을 남겨주세요.

이 기사의 사례를 읽은 후 방법을 마스터했다고 생각합니다. 더 흥미로운 정보를 보려면 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요!

추천 도서:

H5 비디오 재생 라이브러리 video.js 자세한 설명

JS에서 특히 사용하기 쉬운 경량 날짜 플러그인

위 내용은 jQuery, Angular 및 노드의 Promise에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.