jquery和angular都有defer服務,我暫以angular為例談談我的理解,最后並附上jquery的阮一峰總結的defer。
以我目前項目的部分代碼為例說明為什么要用deferred。
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);//位置二 } }
就這段代碼,我想要在位置二處使用base64,然后結果呢?
兩處位置都打印了,位置一得到base64,ok,沒問題。當我在位置二想使用base64時,問題來了?onload隊列的問題,位置二總是無法正確的獲取到想要的base64,這個時候就可以考慮異步問題了。
我相信大家應該和我一樣,遇到這種情況第一反應應該是deferred讓函數異步執行。
那么,我講以上代碼使用deferred之后,問題完美解決!
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對象上傳 });
這里有上傳功能我以前的博客有介紹,可以點擊這里去看看,給點建議!
問題解決了,我又想到了分享!那么我將我的拙見寫出來,請不吝賜教!
什么是defer?
- $q是Angular的一種內置服務,它可以使你異步地執行函數,並且當函數執行完成時或異常時它允許你使用函數的返回值或返回執行狀態通知等。
- defer的意思是延遲,$q.defer() 可以創建一個deferred延遲對象實例,實例旨在暴露派生的Promise 實例,Promise就是一種對執行結果不確定的一種預先定義,如果成功,就xx;如果失敗,就xx,就像事先給出了一些承諾。
用法:
一個最完整的寫法:
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。
這里在探討下暫時很少用的$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返回值!
最后附上所有代碼:

結語:
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