Angular中的$q的形象解释及深刻用法

做者:寸志
连接:https://zhuanlan.zhihu.com/p/19622332
来源:知乎
著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。

javascript

早上,老爸说:“儿子,天气如何?”

每周一早上,老爸问儿子下午的天气状况,儿子能够到自家房子旁边小山上使用望远镜来观看。儿子在出发时许诺(Promise)老爸(会通知老爸天气状况)。
此刻,老爸决定,若是天气不错,明天就出去捕鱼,不然就不去。并且若是儿子没法得到天气预报的话,也不去捕鱼。
30分钟左右,儿子回来了,每周的结局都不同。

html

结局A:成功得到了(retrieved)天气预报,晴天 :)

儿子成功获取了天气预报,天空晴朗,阳光明媚!承诺(Promise)兑现了(resolved),因而老爸决定开始为周日的捕鱼作准备。java

结局B:一样成功得到了天气预报,雨天:(

儿子成功得到了天气预报,只不过是乌云密布,要下雨承诺(Promise)兑现了(resolved),只是老爸决定呆在家里,由于天气很糟糕。angularjs

结局C:无法得到天气预报:-/

出了问题,儿子无法得知天气预报,由于雾很大,就算站在小山上也没法看清。儿子没办法对象他离开时许下的诺言, promise was rejected!老爸决定留下来,这并不值得冒险。编程

从编程的角度来看

在这种状况下,老爸是逻辑控制,他把儿子当作service来处理。
这里面的逻辑咱们已经知道了,老爸要儿子给他预报天气,可是儿子无法立刻告诉他,因而许诺他会带着天气预报回来,老爸在儿子回来以前有其余的事情要作。当老爸从儿子那得到天气预报以后,再决定是打包出发仍是呆在家里。这个故事的重点是,儿子去山顶上看天气这件事并无妨碍(block)老爸干其余的事情。所以对于这种状况,最好的方式就是许诺,随后再兑现或者不兑现。
咱们但是使用Angular的then()指定老爸针对每种结果的对策。then()函数接受两个函数做为参数:一个许诺对现时执行,一个在没法对现时执行。

api

Controller:FatherCtrl

老爸控制状况:数组

// function somewhere in father-controller.js
var makePromiseWithSon = function () {
  // This service's function returns a promise, but we'll deal with that shortly
  SonService.getWeather()
  // then() called when son gets back
  .then(function (data) {
    // promise fulfilled
    if (data.forecast === 'good') {
      prepareFishingTrip();
    } else {
      prepareSundayRoastDinner();
    }
  }, function (error) {
    // promise rejected, could log the error with: console.log('error', error);
    prepareSundayRoastDinner();
  });
};

 

Service:SonServicepromise

儿子就是一个服务,他爬上小山,尝试预报天气。咱们能够把儿子经过望远镜查看即将到来的天气,假设成条用一个天气的API,一般是异步的。要么得到答案,要么出现问题(比方说返回500,大雾皑皑的天空)。
“捕鱼天气API”经过一个许诺返回结果,若是兑现的话,结果的格式像 { "forecast": "good" }这样。app

app.factory('SonService', function ($http, $q) {
  return {
    getWeather: function () {
      // the $http API is based on the deferred/promise APIs exposed by the $q service
      // so it returns a promise for us by default
      return $http.get('http://fishing-weather-api.com/sunday/afternoon')
        .then(function (response) {
          if (typeof response.data === 'object') {
            return response.data;
          } else {
            // invalid response
            return $q.reject(response.data);
          }

        }, function (response) {
          // something went wrong
          return $q.reject(response.data);
        });
    }
  };
});

 

总之

老爸让儿子预报的天气就是一个比喻,说明了异步的本质。老爸并不想在门口傻等着儿子回来,他还有别的事情要作。而是在门口许个诺言,决定在3种不一样的状况(天气好/坏/不知道)下该怎么办,儿子离开时就许下一个诺言,在他回来的时候会兑现或者食言。异步

儿子须要处理一个异步的API(经过望远镜来查看天空/使用天气API)来得到天气,但这些对他老爸来讲都是未知的,是谁不懂科技!?

原文:Promises in AngularJS, Explained as a Cartoon

 

扩展: 看完以后对$q的用法想深刻了,接着看下面,

这篇文章主要介绍了Angular中的Promise对象($q介绍),本文讲解了Promise模式、Q Promise的基本用法、AngularJs中的$q.defferd等内容,须要的朋友能够参考下
 

在用JQuery的时候就知道 promise 是 Js异步编程模式的一种模式,可是不是很明白他跟JQuery的deferred对象有什么区别。随着公司项目的进行,要跟后台接数据了,因此决定搞定它。

Promise

Promise是一种模式,以同步操做的流程形式来操做异步事件,避免了层层嵌套,能够链式操做异步事件。

咱们知道,在编写javascript异步代码时,callback是最最简单的机制,但是用这种机制的话必须牺牲控制流、异常处理和函数语义化为代价,甚至会让咱们掉进出现callback大坑,而promise解决了这个问题。

ES6中Promise、angularJS内置的AngularJS内置Q,以及when采用的都是Promises/A规范,以下:

每一个任务都有三种状态:未完成(pending)、完成(fulfilled)、失败(rejected)。

1.pending状态:能够过渡到履行或拒绝状态。
2.fulfilled状态:不能变为其余任何状态,并且状态不能改变,必须有value值。
3.rejected状态:不能变为其余任何状态,并且状态不能改变,必须有reason。

状态的转移是一次性的,状态一旦变成fulfilled(已完成)或者failed(失败/拒绝),就不能再变了。

 

复制代码代码以下:

function okToGreet(name){
    return name === 'Robin Hood';
}
 
function asyncGreet(name) {
    var deferred = $q.defer();
 
    setTimeout(function() {
     // 由于这个异步函数fn在将来的异步执行,咱们把代码包装到 $apply 调用中,一边正确的观察到 model 的改变
        $scope.$apply(function() {
            deferred.notify('About to greet ' + name + '.');
 
            if (okToGreet(name)) {
                deferred.resolve('Hello, ' + name + '!');
            } else {
                deferred.reject('Greeting ' + name + ' is not allowed.');
            }
        });
    }, 1000);
 
    return deferred.promise;
}
 
var promise = asyncGreet('Robin Hood');
promise.then(function(greeting) {
    alert('Success: ' + greeting);
}, function(reason) {
    alert('Failed: ' + reason);
}, function(update) {
    alert('Got notification: ' + update);
});

 

Q Promise的基本用法

上面代码表示, $q.defer() 构建的 deffered 实例的几个方法的做用。若是异步操做成功,则用resolve方法将Promise对象的状态变为“成功”(即从pending变为resolved);若是异步操做失败,则用reject方法将状态变为“失败”(即从pending变为rejected)。最后返回 deferred.promise ,咱们就能够链式调用then方法。

JS将要有原生Promise,ES6中已经有Promise对象,firefox和Chrome 32 beta版本已经实现了基本的Promise API

AngularJs中的$q.defferd

经过 调用 $q.defferd 返回deffered对象以链式调用。该对象将Promises/A规范中的三个任务状态经过API关联。

deffered API

deffered 对象的方法

1.resolve(value):在声明resolve()处,代表promise对象由pending状态转变为resolve。
2.reject(reason):在声明resolve()处,代表promise对象由pending状态转变为rejected。
3.notify(value) :在声明notify()处,代表promise对象unfulfilled状态,在resolve或reject以前能够被屡次调用。

deffered 对象属性

promise :最后返回的是一个新的deferred对象 promise 属性,而不是原来的deferred对象。这个新的Promise对象只能观察原来Promise对象的状态,而没法修改deferred对象的内在状态能够防止任务状态被外部修改。

Promise API

当建立 deferred 实例时会建立一个新的 promise 对象,并能够经过 deferred.promise 获得该引用。

promise 对象的目的是在 deferred 任务完成时,容许感兴趣的部分取得其执行结果。

promise 对象的方法

1.then(errorHandler, fulfilledHandler, progressHandler):then方法用来监听一个Promise的不一样状态。errorHandler监听failed状态,fulfilledHandler监听fulfilled状态,progressHandler监听unfulfilled(未完成)状态。此外,notify 回调可能被调用 0到屡次,提供一个进度指示在解决或拒绝(resolve和rejected)以前。
2.catch(errorCallback) —— promise.then(null, errorCallback) 的快捷方式
3.finally(callback) ——让你能够观察到一个 promise 是被执行仍是被拒绝, 但这样作不用修改最后的 value值。 这能够用来作一些释放资源或者清理无用对象的工做,无论promise 被拒绝仍是解决。 更多的信息请参阅 完整文档规范.

经过then()方法能够实现promise链式调用。

 

复制代码代码以下:

promiseB = promiseA.then(function(result) {  
  return result + 1;  
});  
 
// promiseB 将会在处理完 promiseA 以后马上被处理,  
// 而且其  value值是promiseA的结果增长1

 

$q的其余方法

$q.when(value):传递变量值,promise.then()执行成功回调
$q.all(promises):多个promise必须执行成功,才能执行成功回调,传递值为数组或哈希值,数组中每一个值为与Index对应的promise对象

 

再深刻点: 

1. $q

$q是Angular的一种内置服务,它可使你异步地执行函数,而且当函数执行完成时它容许你使用函数的返回值(或异常)。

2. defer

defer的字面意思是延迟,$q.defer() 能够建立一个deferred实例(延迟对象实例)。

deferred 实例旨在暴露派生的Promise 实例,以及被用来做为成功完成或未成功完成的信号API,以及当前任务的状态。这听起来好复杂的样子,总结$q, defer, promise三者之间的关系以下所示。

var deferred = $q.defer();  //经过$q服务注册一个延迟对象 deferred
var promise = deferred.promise;  //经过deferred延迟对象,能够获得一个承诺promise,而promise会返回当前任务的完成结果

defer的方法:

    1. deferred.resolve(value)  成功解决(resolve)了其派生的promise。参数value未来会被用做promise.then(successCallback(value){...}, errorCallback(reason){...}, notifyCallback(notify){...})中successCallback函数的参数。

    2. deferred.reject(reason)  未成功解决其派生的promise。参数reason被用来讲明未成功的缘由。此时deferred实例的promise对象将会捕获一个任务未成功执行的错误,promise.catch(errorCallback(reason){...})。补充一点,promise.catch(errorCallback)实际上就是promise.then(null, errorCallback)的简写。

    3. notify(value)  更新promise的执行状态(翻译的很差,原话是provides updates on the status of the promise's execution)

defer的小例子:    

复制代码
function asyncGreet(name) {
  var deferred = $q.defer();  //经过$q.defer()建立一个deferred延迟对象,在建立一个deferred实例时,也会建立出来一个派生的promise对象,使用deferred.promise就能够检索到派生的promise。

  deferred.notify('About to greet ' + name + '.');  //延迟对象的notify方法。

  if (okToGreet(name)) {
    deferred.resolve('Hello, ' + name + '!');  //任务被成功执行
  } else {
    deferred.reject('Greeting ' + name + ' is not allowed.');  //任务未被成功执行
  }

  return deferred.promise;  //返回deferred实例的promise对象
}

function okToGreet(name) {
  //只是mock数据,实际状况将根据相关业务实现代码
  if(name == 'Superman') return true;  
  else return false;
}

var promise = asyncGreet('Superman');  //得到promise对象
//promise对象的then函数会得到当前任务也就是当前deferred延迟实例的执行状态。它的三个回调函数分别会在resolve(), reject() 和notify()时被执行
promise.then(function(greeting) {
  alert('Success: ' + greeting);
}, function(reason) {
  alert('Failed: ' + reason);
}, function(update) {
  alert('Got notification: ' + update);
});
复制代码

3. promise

当建立一个deferred实例时,promise实例也会被建立。经过deferred.promise就能够检索到deferred派生的promise。

promise的目的是容许interested parties 访问deferred任务完成的结果。

按照CommonJS的约定,promise是一个与对象交互的接口,表示一个动做(action)的结果是异步的,并且在任何给定的时间点上可能或不可能完成。(这句话好绕口,个人理解是promise至关于一个承诺,承诺你这个任务在给定的时间点上可能会完成,也可能完成不了。若是完成了那就至关于resolve, 若是未完成就至关于reject。不知道这样理解对不对?)

promise 的方法:

    1. then(successCallback, errorCallback, nitifyCallback) 根据promise被resolve/reject,或将要被resolve/reject,调用successCallback/errorCallback。

    2. catch(errorCallback)  then(null, errorCallback)的缩写。

    3. finally(callback, notifyCallback)

补充说明:

    promise.then()会返回一个新的衍生promise,造成promise链。例如:

promiseB = promiseA.then(function(result) {
  return result + 1;
});

// promiseB will be resolved immediately after promiseA is resolved and its value
// will be the result of promiseA incremented by 1
相关文章
相关标签/搜索