คุณสามารถแก้ไขสัญญา angularjs ก่อนที่จะส่งคืนได้หรือไม่?


125

ฉันพยายามเขียนฟังก์ชันที่ส่งคืนคำสัญญา แต่มีหลายครั้งที่ข้อมูลที่ร้องขอจะพร้อมใช้งานทันที ฉันต้องการที่จะสรุปไว้ในสัญญาเพื่อให้ผู้บริโภคไม่จำเป็นต้องตัดสินใจ

function getSomething(id) {
    if (Cache[id]) {
        var deferred = $q.defer();
        deferred.resolve(Cache[id]); // <-- Can I do this?
        return deferred.promise;
    } else {
        return $http.get('/someUrl', {id:id});
    }
}

และใช้มันดังนี้:

somethingService.getSomething(5).then(function(thing) {
    alert(thing);
});

ปัญหาคือการโทรกลับไม่ดำเนินการสำหรับสัญญาที่แก้ไขไว้ล่วงหน้า นี่เป็นสิ่งที่ถูกต้องหรือไม่? มีวิธีที่ดีกว่านี้ในการจัดการกับสถานการณ์นี้หรือไม่?


10
return $q.when(Cache[id])วิธีที่ง่ายในการเขียนผลตอบแทนในกรณีแรกคือ อย่างไรก็ตามสิ่งนี้ควรได้ผลและโทรกลับทุกครั้งเนื่องจากคุณสร้างสัญญาใหม่ทุกครั้ง
musically_ut


1
crud หนึ่งชั่วโมงในชีวิตของฉันหายไป ฉันกำลังลองสิ่งนี้ในการทดสอบหน่วยและคำสัญญาจะสำเร็จหลังจากการทดสอบเสร็จสมบูรณ์และฉันไม่เห็นมัน มีปัญหากับการทดสอบของฉันไม่ใช่รหัส
Craig Celeste

อย่าลืมโทรหา $ scope $ apply () เพื่อให้แน่ใจว่าสิ่งต่างๆแก้ไขได้ทันทีในระหว่างการทดสอบ
dtabuenc

ฉันคิดว่า httpbackend.flush บัญชีนี้ แต่ $ q อาจไม่ ฉันไม่ได้ใช้ขอบเขตในการทดสอบนี้ ฉันกำลังทดสอบบริการโดยตรง แต่ฉันก็ใช้งานได้ขอบคุณ
Craig Celeste

คำตอบ:


174

คำตอบสั้น ๆ : ใช่คุณสามารถแก้ไขสัญญา AngularJS ก่อนที่คุณจะส่งคืนและมันจะทำงานตามที่คุณคาดหวัง

จากPlunkr ของ JB Nizetแต่ปรับโครงสร้างใหม่ให้ทำงานภายใต้บริบทของสิ่งที่ถามเดิม (เช่นฟังก์ชันเรียกใช้บริการ) และจริงในไซต์

ด้านในบริการ ...

function getSomething(id) {
    // There will always be a promise so always declare it.
    var deferred = $q.defer();
    if (Cache[id]) {
        // Resolve the deferred $q object before returning the promise
        deferred.resolve(Cache[id]); 
        return deferred.promise;
    } 
    // else- not in cache 
    $http.get('/someUrl', {id:id}).success(function(data){
        // Store your data or what ever.... 
        // Then resolve
        deferred.resolve(data);               
    }).error(function(data, status, headers, config) {
        deferred.reject("Error: request returned status " + status); 
    });
    return deferred.promise;

}

ภายในเครื่องควบคุม ....

somethingService.getSomething(5).then(    
    function(thing) {     // On success
        alert(thing);
    },
    function(message) {   // On failure
        alert(message);
    }
);

ฉันหวังว่ามันจะช่วยใครสักคน ฉันไม่พบคำตอบอื่น ๆ ที่ชัดเจน


2
ฉันไม่สามารถอธิบายด้วยคำพูดว่าฉันมีความสุขแค่ไหนคุณช่วยฉันได้มาก h.coates!
rilar

ในกรณีที่ http GET ล้มเหลวสัญญาที่ส่งคืนจะไม่ถูกปฏิเสธด้วยวิธีนี้
lex82

5
ดังนั้น tl; dr สำหรับโพสต์นี้คือ: ใช่คุณสามารถแก้ไขคำสัญญาก่อนที่จะส่งคืนและมันจะลัดวงจรตามที่ตั้งใจไว้
ray

1
คำตอบนี้ยังใช้กับ Q ของ Kris Kowal ซึ่งคำสัญญาของ Angular เป็นไปตาม
Keith

ฉันได้เพิ่มตัวอย่างการจัดการข้อผิดพลาดลงในคำตอบของคุณแล้วฉันหวังว่าจะไม่เป็นไร
Simon East

98

วิธีคืนสัญญาที่แก้ไขไว้ล่วงหน้าใน Angular 1.x

แก้ไขสัญญา:

return $q.when( someValue );    // angular 1.2+
return $q.resolve( someValue ); // angular 1.4+, alias to `when` to match ES6

คำสัญญาที่ถูกปฏิเสธ:

return $q.reject( someValue );

1
ไม่จำเป็นต้องมีโรงงานนั้นฟังก์ชั่นตัวช่วยเหล่านั้นมีให้แล้ว:{resolved: $q.when, rejected: $q.reject}
Bergi

เฮ้ Bergi ขอบคุณสำหรับการบรรยายที่มีค่าของคุณ ฉันได้แก้ไขคำตอบของฉันตามนั้น
Andrey Mikhaylov - lolmaus

2
ฉันคิดว่าคำตอบนี้ควรเลือก
Morteza Tourani

@mortezaT ถ้ามันถูกเลือกมันจะไม่ทำให้ฉันได้เหรียญทอง ;)
Andrey Mikhaylov - lolmaus

6

นี่คือวิธีที่ฉันมักจะทำถ้าฉันต้องการแคชข้อมูลในอาร์เรย์หรือวัตถุ

app.factory('DataService', function($q, $http) {
  var cache = {};
  var service= {       
    getData: function(id, callback) {
      var deffered = $q.defer();
      if (cache[id]) {         
        deffered.resolve(cache[id])
      } else {            
        $http.get('data.json').then(function(res) {
          cache[id] = res.data;              
          deffered.resolve(cache[id])
        })
      }
      return deffered.promise.then(callback)
    }
  }

  return service

})

DEMO


0

คุณลืมเริ่มต้นองค์ประกอบแคช

function getSomething(id) {
    if (Cache[id]) {
        var deferred = $q.defer();
        deferred.resolve(Cache[id]); // <-- Can I do this?
        return deferred.promise;
    } else {
        Cache[id] = $http.get('/someUrl', {id:id});
        return Cache[id];
    }
}

ขอโทษ นั่นคือเรื่องจริง ฉันพยายามทำให้โค้ดง่ายขึ้นเพื่อความชัดเจนในคำถาม ถึงกระนั้นหากเป็นไปตามสัญญาที่ได้รับการแก้ไขไว้แล้วดูเหมือนว่าจะไม่ได้โทรกลับ
Craig Celeste

2
ฉันไม่คิดว่าถ้าคุณแก้คำสัญญาด้วยคำสัญญาคำสัญญาภายในก็จะแบนราบ สิ่งนี้จะเติมCacheคำสัญญาแทนวัตถุที่ต้องการและประเภทการส่งคืนสำหรับกรณีที่วัตถุอยู่ในแคชและเวลาที่ไม่เหมือนกัน ฉันคิดว่าถูกต้องกว่า:$http.get('/someUrl', {id: id}).then(function (response) { Cache[id] = response.data; return Cache[id]; });
musically_ut

0

ฉันชอบใช้โรงงานเพื่อรับข้อมูลจากทรัพยากรของฉันอย่างเช่น

.factory("SweetFactory", [ "$http", "$q", "$resource", function( $http, $q, $resource ) {
    return $resource("/sweet/app", {}, {
        "put": {
            method: "PUT",
            isArray: false
        },"get": {
            method: "GET",
            isArray: false
        }
    });
}]);

จากนั้นเปิดเผยโมเดลของฉันในบริการเช่นนี้ที่นี่

 .service("SweetService",  [ "$q", "$filter",  "$log", "SweetFactory",
    function ($q, $filter, $log, SweetFactory) {

        var service = this;

        //Object that may be exposed by a controller if desired update using get and put methods provided
        service.stuff={
            //all kinds of stuff
        };

        service.listOfStuff = [
            {value:"", text:"Please Select"},
            {value:"stuff", text:"stuff"}];

        service.getStuff = function () {

            var deferred = $q.defer();

          var promise = SweetFactory.get().$promise.then(
                function (response) {
                    if (response.response.result.code !== "COOL_BABY") {
                        deferred.reject(response);
                    } else {
                        deferred.resolve(response);
                        console.log("stuff is got", service.alerts);
                        return deferred.promise;
                    }

                }
            ).catch(
                function (error) {
                    deferred.reject(error);
                    console.log("failed to get stuff");
                }
            );

            promise.then(function(response){
                //...do some stuff to sett your stuff maybe fancy it up
                service.stuff.formattedStuff = $filter('stuffFormatter')(service.stuff);

            });


            return service.stuff;
        };


        service.putStuff = function () {
            console.log("putting stuff eh", service.stuff);

            //maybe do stuff to your stuff

            AlertsFactory.put(service.stuff).$promise.then(function (response) {
                console.log("yep yep", response.response.code);
                service.getStuff();
            }).catch(function (errorData) {
                alert("Failed to update stuff" + errorData.response.code);
            });

        };

    }]);

จากนั้นตัวควบคุมของฉันสามารถรวมไว้และเปิดเผยหรือทำในสิ่งที่มันถูกต้องในบริบทของมันเพียงแค่อ้างถึงบริการที่ฉีดเข้ามา

ดูเหมือนจะทำงานได้ดี แต่ฉันยังใหม่สำหรับเชิงมุม * การจัดการข้อผิดพลาดส่วนใหญ่ทิ้งไว้เพื่อความชัดเจน


getStuffวิธีการของคุณใช้
แอนติแพตเทิร์น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.