خانه / Angular / استفاده از Promiseها در AngularJS

استفاده از Promiseها در AngularJS

استفاده از Promiseها در AngularJS

Promiseها جهت کنترل توابع asynchronous مورد استفاده می شوند و به برنامه نویس اجازه می دهد که چندین function را با هم بصورت chain استفاده کنند. که موجب افزایش خوانایی خواهد شد و ایجاد توابع شخصی بصورت chain باعث می شود که reusability بالاتری دست برسیم.

function fetchData(id, cb){

getDataFromServer(id, function(err, result){

if(err){

cb(err, null);

}else{

transformData(result, function(err, transformedResult){

if(err){

cb(err, null);

}else{

saveToIndexDB(result, function(err, savedData){

cb(err, savedData);

});}});}

});}

function fetchData(id){

return getDataFromServer(id)

.then(transformData)

.then(saveToIndexDB);

}

بعلاوه بمنظور افزایش خوانایی؛ Promiseها به همراه error handling و progress updates و تمپلت های AngularJS می توانند کمک کننده باشند.

Handling Errors

در صورتی که fetchData صدا زده شود و exceptionای در در توابع transformData() یا  saveToIndexDB()  این بین raise شود در نتیجه error callback پایانی فعال خواهد شد.

fetchData(1)

.then(function(result){

}, function(error){

// exceptions in transformData, or saveToIndexDB

// will result in this error callback being called.

});

متاسفانه؛ اگر exceptionای در getDataFromServer()  رخ بدهد؛ در اینجا error callback پایاینی trigger نخواهد شد. و این مورد به این دلیل است که transformData() و  saveToIndexDB() به همراه و درون context مرتبط با then() فراخوانی شده است, که از مکانیزم try-catch استفاده می کند و بصورت اتوماتیک در حالث بروز خطا .reject() را صدا می زند. به جهت از بین بردن این رفتار در تابع نخستین می توانیم بلاک try-catch رو بصورت زیر تعریف نمانیم:

function getDataFromServer(id){

var deferred = $q.defer();

try{

// asynchronous function, which calls

// deferred.resolve() on sucess

}catch(e){

deferred.reject(e);

}

return deferred.promise;

}

هرچند افزودن try-catch درون تابع  getDataFromServer() موجب می شود که ظرافت و زیبایی آن پایین بیاید, اما این تابع را قوی تر کرده و آن را جهت استفاده به عنوان اولین تابع در زنجیره ی Promiseها (Chain of Promises) مناسب تر و راحت تر می کند.

Using Notify for Progress Updates

Promise فقط می تواند resolved یا rejected باشد. به منظور مهیا کردن Progress Update؛ که ممکن است چندین مرتبه اتفاق بیافتد یا اصلا اتفاق نیافتد, یک Promise همچنین شامل notify callback که در AngularJS 1.2+ معرفی شده؛ می باشد.

Notify می تواند بر روی یک task غیر همزمان طولانی مدت-long running asynchronous task؛ progress update به تدریج افزایشی مهیا کند. در زیر مثالی از یک تابع long running آورد شده است-processLotsOfData()؛ که از notify بمنظور ارائه ی progress updates استفاده می کند.

function processLotsOfData(data){

var output = [],

deferred = $q.defer(),

percentComplete = 0;

for(var i = 0; i < data.length; i++){

output.push(processDataItem(data[i]));

percentComplete = (i+1)/data.length * 100;

deferred.notify(percentComplete);

}

deferred.resolve(output);

return deferred.promise;

};

processLotsOfData(data)

.then(function(result){

// success

}, function(error){

// error

}, function(percentComplete){

$scope.progress = percentComplete;

});

با استفاده از تابع notify() می توانیم بروز رسانی های زیادی را روی متغیر progress دررون $scope قبل از اینکه processLotsOfData به پایان برسد(resolved) داشته باشیم؛ در نتیجه notify() برای progress barها بسیار ایده آل است.

متاسفانه, استفاده از notify() دررون chain یا promiseها سنگین است چون فراخوانی notify() بصورت bubble up به لایه های بالایی سرایت پیدا نمی کند. هر تابع درون chain باید بصورت دستی notificationها را به لایه ی بعدی در chain بفرستد؛ که در نتیجه موجب می شود که خوانایی کد پایین بیاید.

 

Templates

 

Templateهای AngularJS؛ Promiseها را تشخیص می دهند و فرآیند rendering را می توانند تا زمان به پایان رسیدن Promise به تاخیر بیاندازند (resolved or rejected). Templateهای AngularJS دیگر Promiseها را تشخیص نمی دهند. – و انها باید بصورت دستی درن کنترلر قبل از اینکه به $scope مرتبط assign شوند کنترل و handle شوند. به عنوان نمونه فرض کنید که template ما شبیه به زیر می باشد:

 

<p>{{bio}}</p>

پس ما می توانیم بصورت زیر در کنترلر عمل کنیم:

function getBio(){

var deferred = $q.defer();

// async call, resolved after ajax request completes

return deferred.promise;

};

getBio().then(function(bio){

$scope.bio = bio;

});

محدودیت های Promise در AngularJS

 

هنگامی که یک Promise بصورت غیر همزمان به resolved رسید .resolved() باید درون یک تابع wrap شود. در مثال پائین, کاربر بر روی دکمه ی goodby() کلیک می کندکه باید ویژگی greeting مربوط به $scope را update کند.

app.controller(‘AppCtrl’,

[   ‘$scope’,

‘$q’,

function AppCtrl($scope, $q){

$scope.greeting = “hello”;

var updateGreeting = function(message){

var deferred = $q.defer();

setTimeout(function(){

deferred.resolve(message);

}, 5);

return deferred.promise;

};

$scope.goodbye = function(){

$scope.greeting = updateGreeting(‘goodbye’);

}

}]);

متاسفانه این کد بشکلی که انتظار می رود, عمل نمی کند؛ زیرا رویداد غیر همزمان خارج از event loop مربوط به AngularJS اتفاق افتاده است. بمنظور حل این مورد, باید resolve مربوط به deffere رو درون $scope.$apply فراخوانی کنیم تا اینکه چرخه ی digest را مجددا trigger کند و در نتیجه $scope را بروزرسانی نماید.

setTimeout(function(){

$scope.$apply(function(){

deferred.resolve(message);

});

}, 5)

درباره ی masoud@admin

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

در تلگرام هم همراه شما هستم

اگر علاقمند به معماری نرم افزار و مبحث محبوب مایکروسرویس هستید؛ در کانال با ما همراه باشید. اطلاعات مفید زیادی در این کانال انتظار شما را می کشند. فقط کافیست دکمه ی پیوستن را بفشارید.

پیوستن بستن