จะรอจนกว่าคำตอบจะมาจากคำขอ $ http ใน angularjs ได้อย่างไร


94

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

var myApp =  angular.module('myservices', []);

myApp.factory('myService', function($http) {
    $http({method:"GET", url:"/my/url"}).success(function(result){
        return result;
    });
});

ในคอนโทรลเลอร์ของฉันฉันใช้บริการนี้ในฐานะ:

function myFunction($scope, myService) {
    $scope.data = myService;
    console.log("data.name"+$scope.data.name);
}

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

คำตอบ:


151

คุณควรใช้สัญญาสำหรับการดำเนินการ async โดยที่คุณไม่รู้ว่าจะเสร็จสิ้นเมื่อใด คำสัญญา "แสดงถึงการดำเนินการที่ยังไม่เสร็จสิ้น แต่คาดว่าจะเกิดขึ้นในอนาคต" ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise )

ตัวอย่างการใช้งานจะเป็นดังนี้:

myApp.factory('myService', function($http) {

    var getData = function() {

        // Angular $http() and then() both return promises themselves 
        return $http({method:"GET", url:"/my/url"}).then(function(result){

            // What we return here is the data that will be accessible 
            // to us after the promise resolves
            return result.data;
        });
    };


    return { getData: getData };
});


function myFunction($scope, myService) {
    var myDataPromise = myService.getData();
    myDataPromise.then(function(result) {  

       // this is only run after getData() resolves
       $scope.data = result;
       console.log("data.name"+$scope.data.name);
    });
}

แก้ไข: เกี่ยวกับความคิดเห็นของ Sujoys ว่า ฉันต้องทำอย่างไรเพื่อที่การเรียก myFuction () จะไม่กลับมาจนกว่าฟังก์ชัน. then () จะเสร็จสิ้นการทำงาน

function myFunction($scope, myService) { 
    var myDataPromise = myService.getData(); 
    myDataPromise.then(function(result) { 
         $scope.data = result; 
         console.log("data.name"+$scope.data.name); 
    }); 
    console.log("This will get printed before data.name inside then. And I don't want that."); 
 }

สมมติว่าการเรียก getData () ใช้เวลา 10 วินาทีจึงจะเสร็จสมบูรณ์ หากฟังก์ชันไม่ส่งคืนอะไรเลยในช่วงเวลานั้นฟังก์ชันจะกลายเป็นรหัสซิงโครนัสปกติอย่างมีประสิทธิภาพและจะหยุดเบราว์เซอร์จนกว่าจะเสร็จ

แม้ว่าสัญญาจะกลับมาทันทีเบราว์เซอร์จึงมีอิสระที่จะดำเนินการต่อโดยใช้รหัสอื่น ๆ เมื่อสัญญาแก้ไข / ล้มเหลวการเรียก then () จะถูกทริกเกอร์ ดังนั้นมันจึงสมเหตุสมผลมากกว่าด้วยวิธีนี้แม้ว่ามันอาจทำให้การไหลของโค้ดของคุณซับซ้อนขึ้นเล็กน้อย (ความซับซ้อนเป็นปัญหาทั่วไปของการเขียนโปรแกรมแบบ async / parallel โดยทั่วไป!)


2
สิ่งนี้ช่วยแก้ปัญหาของฉันได้ !!! สำหรับคนอื่นฉันมีดรอปดาวน์ที่ต้องการข้อมูลจากการโทรของ ajax ดังนั้นเมื่อสร้างขอบเขตแล้วข้อมูลจึงไม่พร้อมใช้งาน ด้วยการเลื่อนเวลานี้ขอบเขตสามารถกำหนดให้มีข้อมูลที่มาจากการโทรของ ajax
Kat Lim Ruiz

8
@mikel: ฉันมีคำถามอื่นที่นี่ การโทร myFuction () ของคุณจะกลับมาทันที แต่สัญญานั้นจากนั้น () จะโทรในภายหลัง ฉันต้องทำอย่างไรเพื่อให้การเรียก myFuction () ไม่กลับมาจนกว่าฟังก์ชัน. then () จะสิ้นสุดการทำงาน function myFunction($scope, myService) { var myDataPromise = myService.getData(); myDataPromise.then(function(result) { $scope.data = result; console.log("data.name"+$scope.data.name); }); console.log("This will get printed before data.name inside then. And I don't want that."); }
Sujoy

13

สำหรับผู้ที่เพิ่งเริ่มใช้สิ่งนี้คุณสามารถใช้การโทรกลับได้เช่น:

ในบริการของคุณ:

.factory('DataHandler',function ($http){

   var GetRandomArtists = function(data, callback){
     $http.post(URL, data).success(function (response) {
         callback(response);
      });
   } 
})

ในตัวควบคุมของคุณ:

    DataHandler.GetRandomArtists(3, function(response){
      $scope.data.random_artists = response;
   });

ทางออกที่ดี คิดตามแนวเดียวกันนี้เมื่อฉันมองเข้าไป ดีใจที่มีคนเอาสิ่งนี้ออกไป
เนท

0

FYI นี่ใช้ Angularfire ดังนั้นอาจแตกต่างกันเล็กน้อยสำหรับบริการอื่นหรือการใช้งานอื่น ๆ แต่ควรแก้ปัญหา isse $ http เดียวกัน ฉันมีปัญหาเดียวกันนี้ทางออกเดียวที่เหมาะกับฉันที่สุดคือรวมบริการ / โรงงานทั้งหมดไว้ในสัญญาเดียวในขอบเขต ในแต่ละเส้นทาง / มุมมองที่จำเป็นต้องโหลดบริการ / etc เหล่านี้ฉันใส่ฟังก์ชั่นใด ๆ ที่ต้องการข้อมูลที่โหลดไว้ภายในฟังก์ชันคอนโทรลเลอร์เช่น myfunct () และ app.js หลักที่รันหลังจาก auth ฉันใส่

myservice.$loaded().then(function() {$rootScope.myservice = myservice;});

และในมุมมองที่ฉันเพิ่งทำ

ng-if="myservice" ng-init="somevar=myfunct()"

ในองค์ประกอบมุมมองแรก / พาเรนต์ / wrapper เพื่อให้คอนโทรลเลอร์สามารถเรียกใช้ทุกอย่างภายใน

myfunct()

โดยไม่ต้องกังวลเกี่ยวกับปัญหา async สัญญา / คำสั่งซื้อ / คิว ฉันหวังว่าจะช่วยคนที่มีปัญหาเดียวกันกับฉัน


0

ฉันประสบปัญหาเดียวกันและไม่มีเลยถ้าสิ่งเหล่านี้ได้ผลสำหรับฉัน นี่คือสิ่งที่ได้ผล ...

app.factory('myService', function($http) {
    var data = function (value) {
            return $http.get(value);
    }

    return { data: data }
});

แล้วฟังก์ชั่นที่ใช้คือ ...

vm.search = function(value) {

        var recieved_data = myService.data(value);

        recieved_data.then(
            function(fulfillment){
                vm.tags = fulfillment.data;
            }, function(){
                console.log("Server did not send tag data.");
        });
    };

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

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