详解Angular.js的$q.defer()服务异步管理

首先本文以个人目前项目的部分代码为例说明为什么要用deferred。

  jquery和angular都有defer服务,我暂以angular为例谈谈我的理解,最后并附上jquery的阮一峰总结的defer。

1. $q

function getBase64(img){//传入图片路径,返回base64
   function getBase64Image(img,width,height) {
    var canvas = document.createElement("canvas");
    canvas.width = width ? width : img.width;
    canvas.height = height ? height : img.height;

    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    var dataURL = canvas.toDataURL();
    return dataURL;
   }
   var image = new Image();
   image.crossOrigin = '';
   image.src = img;

   var base64='';
   if(img){
    image.onload =function (){
     base64=getBase64Image(image);
     console.log(base64);//位置一
    }
    console.log(base64);//位置二
   }
  }

金沙js333娱乐场,  以我目前项目的部分代码为例说明为什么要用deferred。

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

就这段代码,我想要在位置二处使用base64,然后结果呢?

function getBase64(img){//传入图片路径,返回base64
           function getBase64Image(img,width,height) {
               var canvas = document.createElement("canvas");
               canvas.width = width ? width : img.width;
               canvas.height = height ? height : img.height;

               var ctx = canvas.getContext("2d");
               ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
               var dataURL = canvas.toDataURL();
               return dataURL;
           }
           var image = new Image();
           image.crossOrigin = '';
           image.src = img;

           var base64='';
           if(img){
               image.onload =function (){
                   base64=getBase64Image(image);
                   console.log(base64);//位置一
               }
               console.log(base64);//位置二
           }
       }

2. defer

两处位置都打印了,位置一得到base64,ok,没问题。当我在位置二想使用base64时,问题来了?onload队列的问题,位置二总是无法正确的获取到想要的base64,这个时候就可以考虑异步问题了。

  就这段代码,我想要在位置二处使用base64,然后结果呢?

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

我相信大家应该和我一样,遇到这种情况第一反应应该是deferred让函数异步执行。

  两处位置都打印了,位置一得到base64,ok,没问题。当我在位置二想使用base64时,问题来了?onload队列的问题,位置二总是无法正确的获取到想要的base64,这个时候就可以考虑异步问题了。

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

那么,我讲以上代码使用deferred之后,问题完美解决!

  我相信大家应该和我一样,遇到这种情况第一反应应该是deferred让函数异步执行。

var deferred = $q.defer(); //通过$q服务注册一个延迟对象 deferred
var promise = deferred.promise; //通过deferred延迟对象,可以得到一个承诺promise,而promise会返回当前任务的完成结果
function getBase64(img){//传入图片路径,返回base64
    function getBase64Image(img,width,height) {
     var canvas = document.createElement("canvas");
     canvas.width = width ? width : img.width;
     canvas.height = height ? height : img.height;
     var ctx = canvas.getContext("2d");
     ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
     var dataURL = canvas.toDataURL();
     return dataURL;
    }
    var image = new Image();
    image.crossOrigin = '';
    image.src = img;

    var deferred=$q.defer();
    if(img){
     image.onload =function (){
     deferred.resolve(getBase64Image(image));
     }
     return deferred.promise;
    }
    }

getBase64('https://img.alicdn.com/bao/uploaded/TB1qimQIpXXXXXbXFXXSutbFXXX.jpg')
    .then(function(base64){
     var binaryblob = function (s, type) {//blob对象
      var byteString = atob(s);
      var array = [];
      for (var i = 0; i < byteString.length; i++) {
       array.push(byteString.charCodeAt(i));
      }
      return new Blob([new Int8Array(array)], {type: type});
     };
     var binaryPictureBlob = function (dataUrl, filterHead) {//上传base64去头
      var s = filterHead ? dataUrl.replace(/^data:image\/(png|jpeg|pjpeg|bmp|gif|x-png);base64,/, "") : dataUrl;
      return binaryblob(s, "image/jpeg");
     };
     var pic=binaryPictureBlob(base64,true);//blob对象
     //然后调接口将blob对象上传
    });

  那么,我讲以上代码使用deferred之后,问题完美解决!

defer的方法:

问题解决了,我又想到了分享!那么我将我的拙见写出来,请不吝赐教!

function getBase64(img){//传入图片路径,返回base64
                function getBase64Image(img,width,height) {
                  var canvas = document.createElement("canvas");
                  canvas.width = width ? width : img.width;
                  canvas.height = height ? height : img.height;
                  var ctx = canvas.getContext("2d");
                  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
                  var dataURL = canvas.toDataURL();
                  return dataURL;
                }
                var image = new Image();
                image.crossOrigin = '';
                image.src = img;

                var deferred=$q.defer();
                if(img){
                  image.onload =function (){
                    deferred.resolve(getBase64Image(image));
                  }
                  return deferred.promise;
                }
              }

getBase64('https://img.alicdn.com/bao/uploaded/TB1qimQIpXXXXXbXFXXSutbFXXX.jpg')
               .then(function(base64){
                   var binaryblob = function (s, type) {//blob对象
                       var byteString = atob(s);
                       var array = [];
                       for (var i = 0; i < byteString.length; i++) {
                           array.push(byteString.charCodeAt(i));
                       }
                       return  new Blob([new Int8Array(array)], {type: type});
                   };
                   var binaryPictureBlob = function (dataUrl, filterHead) {//上传base64去头
                       var s = filterHead ? dataUrl.replace(/^data:image\/(png|jpeg|pjpeg|bmp|gif|x-png);base64,/, "") : dataUrl;
                       return binaryblob(s, "image/jpeg");
                   };
                   var pic=binaryPictureBlob(base64,true);//blob对象
                   //然后调接口将blob对象上传
               });
  • deferred.resolve(value) 
    成功解决(resolve)了其派生的promise。参数value将来会被用作promise.then(successCallback(value){…},
    errorCallback(reason){…},
    notifyCallback(notify){…})中successCallback函数的参数。
  • deferred.reject(reason) 
    未成功解决其派生的promise。参数reason被用来说明未成功的原因。此时deferred实例的promise对象将会捕获一个任务未成功执行的错误,promise.catch(errorCallback(reason){…})。补充一点,promise.catch(errorCallback)实际上就是promise.then(null,
    errorCallback)的简写。
  • notify(value)  更新promise的执行状态(翻译的不好,原话是provides
    updates on the status of the promise’s execution)

什么是defer?

这里有上传功能我以前的博客有介绍,可以点击这里去看看,给点建议!

defer的小例子:   

$q是Angular的一种内置服务,它可以使你异步地执行函数,并且当函数执行完成时或异常时它允许你使用函数的返回值或返回执行状态通知等。

  问题解决了,我又想到了分享!那么我将我的拙见写出来,请不吝赐教!

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);
});

defer的意思是延迟,$q.defer()可以创建一个deferred延迟对象实例,实例旨在暴露派生的Promise
实例,Promise就是一种对执行结果不确定的一种预先定义,如果成功,就xx;如果失败,就xx,就像事先给出了一些承诺。

  什么是defer?

  • $q是Angular的一种内置服务,它可以使你异步地执行函数,并且当函数执行完成时或异常时它允许你使用函数的返回值或返回执行状态通知等。
  • defer的意思是延迟,$q.defer() 可以创建一个deferred延迟对象实例,实例旨在暴露派生的Promise
    实例,Promise就是一种对执行结果不确定的一种预先定义,如果成功,就xx;如果失败,就xx,就像事先给出了一些承诺。

3. promise

用法:

  用法:

一个最完整的写法:

var defer1 = $q.defer();

       function fun() {
           var deferred = $q.defer();
           $timeout(function () {
               deferred.notify("notify");
               if (iWantResolve) {
                   deferred.resolve("resolved");
               } else {
                   deferred.reject("reject");
               }
           }, 500);
           return deferred.promise;
       }

       $q.when(fun())
               .then(function(success){
                   console.log("success");
                   console.log(success);
               },function(err){
                   console.log("error");
                   console.log(err);
               },function(notify){
                   console.log("notify");
                   console.log(notify);
               })
               .catch(function(reson){
                   console.log("catch");
                   console.log(reson);
               })
               .finally(function(final){
                   console.log('finally');
                   console.log(final);
               });

1、通过$q服务注册一个延迟对象

var deferred=$q.defer();

 
2、成功解决(resolve)了其派生的promise。参数value将来会被用作successCallback(success){}函数的参数value。

 deferred.resolve(success)

 3、未成功解决其派生的promise。参数reason被用来说明未成功的原因。此时deferred实例的promise对象将会捕获一个任务未成功执行的错误,promise.catch(errorCallback(reason){…})。

 deferred.reject(reason) 

 4、更新promise的执行状态通知

deferred.notify("notify");

5、对promise进行处理

 $q.when(fun())
               .then(function(success){
                   console.log("success");
                   console.log(success);
               },function(err){
                   console.log("error");
                   console.log(err);
               },function(notify){
                   console.log("notify");
                   console.log(notify);
               })
               .catch(function(reson){
                   console.log("catch");
                   console.log(reson);
               })
               .finally(function(final){
                   console.log('finally');
                   console.log(final);
               });

这里一般简写为:

fun().then(successCallback, errorCallback, notifyCallback);

注:

  deferred的方法中的参数都返回给了promise与callback的参数都是一一对应的,如:

  deferred.resolve(success)的success对应successCallback(success)的success。

当创建一个deferred实例时,promise实例也会被创建。通过deferred.promise就可以检索到deferred派生的promise。

一个最完整的写法:

这里在探讨下暂时很少用的$q.all().

  $q.all()在多个promise必须执行成功后才能执行成功回调,传递值为数组或哈希值,数组中每个值为与Index对应的promise对象。

  这个方法可以将每个promise里的某些重复代码或者判断,只需要在$q.all()的回调处理一次即可,简化了代码与工作量。

  写法为:

var iWantResolve = true;//没有实际意思,测试运行resolve或reject

       function promise1() {
           return $q(function (resolve, reject) {
               $timeout(function () {
                   if (iWantResolve) {
                       resolve("promise1 resolved");
                   } else {
                       reject("promise1 reject");
                   }
               }, 1000)
           })
       }
       promise1()
               .then(function (s1) {//success callback
                   console.log(s1);
               })
               .catch(function (err1) {//error callback
                   console.log(err1);
               });

       function promise2() {
           var deferred = $q.defer();
           $timeout(function () {
               deferred.notify("promise2 notify");
               if (iWantResolve) {
                   deferred.resolve("promise2 resolved");
               } else {
                   deferred.reject("promise2 reject");
               }
           }, 500);
           return deferred.promise;
       }

       promise2()
               .then(function (s2) {
                   console.log(s2);
               }, function (err2) {
                   console.log(err2);
               });

       $q.all([promise1(), promise2()])
               .then(function (dataArr) {
                   //promise都成功执行后的回调函数
                   console.log("$q.all: ", dataArr);
               }, function (err) {
                   console.log("$q.all: ", err)
               });

  像这个例子,每个promise回调都打印了返回值,那么可以用$q.all()处理在其回调打印dataArr,则包含了所有promise返回值!

最后附上所有代码:

金沙js333娱乐场 1View Code

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

var defer1 = $q.defer();

  function fun() {
   var deferred = $q.defer();
   $timeout(function () {
    deferred.notify("notify");
    if (iWantResolve) {
     deferred.resolve("resolved");
    } else {
     deferred.reject("reject");
    }
   }, 500);
   return deferred.promise;
  }

  $q.when(fun())
    .then(function(success){
     console.log("success");
     console.log(success);
    },function(err){
     console.log("error");
     console.log(err);
    },function(notify){
     console.log("notify");
     console.log(notify);
    })
    .catch(function(reson){
     console.log("catch");
     console.log(reson);
    })
    .finally(function(final){
     console.log('finally');
     console.log(final);
    });

  结语:

  jquery和angular的deferred用法大致相同,但有两处要注意的地方:

  jquery:

defer=$.Deferred();
defer.promise();

  angular:

var deferred=$q.defer();
deferred.promise;

  以上便是我对angular的$q、deferred、promise的一些浅显的理解,望各位大神多多评论、指教……

最后附上:

jquery中文网的deferred介绍:

http://www.jquery123.com/category/deferred-object/

一位大神对jquery的deferred的总结!

阮一峰:http://www.ruanyifeng.com/blog/2011/08/a\_detailed\_explanation\_of\_jquery\_deferred\_object.html

 

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

1、通过$q服务注册一个延迟对象

promise 的方法:

var deferred=$q.defer();
  • then(successCallback, errorCallback, nitifyCallback)
    根据promise被resolve/reject,或将要被resolve/reject,调用successCallback/errorCallback。
  • catch(errorCallback)  then(null, errorCallback)的缩写。
  • finally(callback, notifyCallback)

2、成功解决(resolve)了其派生的promise。参数value将来会被用作successCallback(success){}函数的参数value。

补充说明:

发表评论

电子邮件地址不会被公开。 必填项已用*标注