นี่ไม่ใช่คำตอบที่สมบูรณ์สำหรับคำถามของคุณ แต่หวังว่าจะช่วยให้คุณและผู้อื่นเมื่อคุณพยายามอ่านเอกสารใน$qบริการ ฉันใช้เวลาสักครู่เพื่อทำความเข้าใจ
ลองแยก AngularJS สักครู่แล้วลองพิจารณาการโทรผ่าน Facebook API การเรียก API ทั้งสองใช้กลไกการโทรกลับเพื่อแจ้งผู้โทรเมื่อมีการตอบกลับจาก Facebook:
  facebook.FB.api('/' + item, function (result) {
    if (result.error) {
      // handle error
    } else {
      // handle success
    }
  });
  // program continues while request is pending
  ...
นี่เป็นรูปแบบมาตรฐานสำหรับการจัดการการดำเนินการแบบอะซิงโครนัสใน JavaScript และภาษาอื่น ๆ
ปัญหาใหญ่ที่เกิดขึ้นกับรูปแบบนี้เกิดขึ้นเมื่อคุณต้องการดำเนินการลำดับของการดำเนินการแบบอะซิงโครนัสซึ่งการดำเนินการที่ต่อเนื่องแต่ละครั้งขึ้นอยู่กับผลลัพธ์ของการดำเนินการก่อนหน้านี้ นั่นคือสิ่งที่รหัสนี้กำลังทำ:
  FB.login(function(response) {
      if (response.authResponse) {
          FB.api('/me', success);
      } else {
          fail('User cancelled login or did not fully authorize.');
      }
  });
ก่อนอื่นก็พยายามเข้าสู่ระบบและหลังจากตรวจสอบแล้วว่าการเข้าสู่ระบบสำเร็จนั้นจะทำการร้องขอไปยังกราฟ API
แม้ในกรณีนี้ซึ่งเป็นการผูกมัดเพียงสองอย่างเข้าด้วยกันสิ่งต่าง ๆ ก็เริ่มยุ่งเหยิง วิธีนี้askFacebookForAuthenticationยอมรับการเรียกกลับสำหรับความล้มเหลวและความสำเร็จ แต่เกิดอะไรขึ้นเมื่อFB.loginสำเร็จ แต่FB.apiล้มเหลว วิธีการนี้จะเรียกการsuccessเรียกกลับโดยไม่คำนึงถึงผลของFB.apiวิธีการ  
ตอนนี้ลองนึกภาพว่าคุณกำลังพยายามเขียนรหัสลำดับที่มีประสิทธิภาพของการทำงานแบบอะซิงโครนัสตั้งแต่สามครั้งขึ้นไปในวิธีที่จัดการข้อผิดพลาดได้อย่างถูกต้องในแต่ละขั้นตอนและสามารถอ่านได้กับคนอื่น ๆ เป็นไปได้ แต่เป็นเรื่องง่ายมากที่จะเพียงแค่วางซ้อนการเรียกกลับเหล่านั้นและติดตามข้อผิดพลาดไปพร้อมกัน
ทีนี้ลองแยก Facebook API สักครู่แล้วลองพิจารณา Angular Promises API ตามที่$qบริการดำเนินการ รูปแบบที่ดำเนินการโดยบริการนี้เป็นความพยายามที่จะเปลี่ยนการเขียนโปรแกรมแบบอะซิงโครนัสกลับไปเป็นสิ่งที่คล้ายกับชุดข้อความเชิงเส้นตรงซึ่งมีความสามารถในการ 'โยน' ข้อผิดพลาดในขั้นตอนใด ๆtry/catchบล็อกที่คุ้นเคย
ลองพิจารณาตัวอย่างที่ประดิษฐ์ขึ้นนี้ สมมติว่าเรามีสองฟังก์ชันโดยที่ฟังก์ชันที่สองใช้ผลลัพธ์ของฟังก์ชันแรก:
 var firstFn = function(param) {
    // do something with param
    return 'firstResult';
 };
 var secondFn = function(param) {
    // do something with param
    return 'secondResult';
 };
 secondFn(firstFn()); 
ทีนี้ลองนึกภาพว่า firstFn และ secondFn ทั้งคู่ใช้เวลานานกว่าจะเสร็จสมบูรณ์ดังนั้นเราจึงต้องการประมวลผลลำดับนี้แบบอะซิงโครนัส ครั้งแรกที่เราสร้างdeferredวัตถุใหม่ซึ่งแสดงถึงห่วงโซ่ของการดำเนินการ:
 var deferred = $q.defer();
 var promise = deferred.promise;
promiseทรัพย์สินหมายถึงผลที่สุดของห่วงโซ่ หากคุณบันทึกสัญญาทันทีหลังจากสร้างคุณจะเห็นว่าเป็นเพียงวัตถุว่างเปล่า ( {}) ยังไม่มีอะไรให้ดูเลยไปทางขวา
จนถึงคำสัญญาของเราเพียงแสดงจุดเริ่มต้นในห่วงโซ่ ทีนี้ลองเพิ่มการดำเนินการสองอย่างของเรา:
 promise = promise.then(firstFn).then(secondFn);
thenวิธีการเพิ่มขั้นตอนในการห่วงโซ่และจากนั้นส่งกลับสัญญาใหม่ที่เป็นตัวแทนของผลที่สุดของห่วงโซ่การขยาย คุณสามารถเพิ่มขั้นตอนได้มากเท่าที่คุณต้องการ
จนถึงตอนนี้เราได้ตั้งค่าฟังก์ชั่นโซ่ แต่ก็ไม่มีอะไรเกิดขึ้นจริง คุณได้รับสิ่งที่เริ่มต้นด้วยการโทรdeferred.resolveระบุค่าเริ่มต้นที่คุณต้องการผ่านไปยังขั้นตอนแรกที่แท้จริงในห่วงโซ่:
 deferred.resolve('initial value');
แล้ว ... ยังไม่มีอะไรเกิดขึ้น เพื่อให้แน่ใจว่ามีการตรวจสอบการเปลี่ยนแปลงรูปแบบอย่างเหมาะสม Angular ไม่ได้เรียกขั้นตอนแรกในห่วงโซ่จริงจนกว่า$applyจะมีการเรียกใช้ครั้งต่อไป:
 deferred.resolve('initial value');
 $rootScope.$apply();
 // or     
 $rootScope.$apply(function() {
    deferred.resolve('initial value');
 });
แล้วการจัดการข้อผิดพลาดล่ะ? จนถึงตอนนี้เราได้ระบุตัวจัดการความสำเร็จในแต่ละขั้นตอนในห่วงโซ่เท่านั้น  thenยังยอมรับตัวจัดการข้อผิดพลาดเป็นอาร์กิวเมนต์ตัวเลือกที่สอง นี่คืออีกตัวอย่างหนึ่งที่ยาวกว่าของเครือสัญญาในครั้งนี้ที่มีการจัดการข้อผิดพลาด:
 var firstFn = function(param) {
    // do something with param
    if (param == 'bad value') {
      return $q.reject('invalid value');
    } else {
      return 'firstResult';
    }
 };
 var secondFn = function(param) {
    // do something with param
    if (param == 'bad value') {
      return $q.reject('invalid value');
    } else {
      return 'secondResult';
    }
 };
 var thirdFn = function(param) {
    // do something with param
    return 'thirdResult';
 };
 var errorFn = function(message) {
   // handle error
 };
 var deferred = $q.defer();
 var promise = deferred.promise.then(firstFn).then(secondFn).then(thirdFn, errorFn);
อย่างที่คุณเห็นในตัวอย่างนี้แต่ละ handler ใน chain มีโอกาสที่จะเบี่ยงเบนการรับส่งข้อมูลไปยังตัวจัดการข้อผิดพลาดถัดไปแทนที่จะเป็นhandler ที่ประสบความสำเร็จต่อไป ในกรณีส่วนใหญ่คุณสามารถมีตัวจัดการข้อผิดพลาดเดียวที่ส่วนท้ายของเชน แต่คุณสามารถมีตัวจัดการข้อผิดพลาดระดับกลางที่พยายามกู้คืน
หากต้องการกลับไปที่ตัวอย่างของคุณ (และคำถามของคุณ) อย่างรวดเร็วฉันจะบอกว่าพวกเขาเป็นตัวแทนของสองวิธีที่แตกต่างกันในการปรับ API การติดต่อกลับของ Facebook ให้สอดคล้องกับวิธีของ Angular ในการสังเกตการเปลี่ยนแปลงรูปแบบ ตัวอย่างแรกล้อมรอบการเรียก API ในสัญญาซึ่งสามารถเพิ่มในขอบเขตและเข้าใจได้โดยระบบ templating ของ Angular ครั้งที่สองใช้วิธีการบังคับเดรัจฉานมากขึ้นในการตั้งค่าผลลัพธ์การโทรกลับโดยตรงบนขอบเขตแล้วเรียก$scope.$digest()ให้ Angular รับรู้การเปลี่ยนแปลงจากแหล่งภายนอก  
ตัวอย่างสองตัวอย่างนั้นไม่สามารถเปรียบเทียบกันได้โดยตรงเนื่องจากขั้นตอนแรกไม่มีขั้นตอนการลงชื่อเข้าใช้ อย่างไรก็ตามโดยทั่วไปเป็นที่ต้องการในการสรุปการโต้ตอบกับ API ภายนอกเช่นนี้ในบริการแยกต่างหากและส่งมอบผลลัพธ์ให้กับตัวควบคุมตามที่สัญญาไว้ ด้วยวิธีนี้คุณสามารถแยกตัวควบคุมออกจากข้อกังวลภายนอกและทดสอบได้ง่ายขึ้นด้วยบริการจำลอง