In che modo la funzione $ resource `get` funziona in modo sincrono in AngularJS?

Stavo guardando questo tutorial di AngularJS che descrive come collegarsi a Twitter con una risorsa angular. ( Video tutorial ) Ecco la risorsa impostata nel controller di esempio:

$scope.twitter = $resource('http://twitter.com/:action', {action: 'search.json', q: 'angularjs', callback: 'JSON_CALLBACK'}, {get: {method: 'JSONP'}}); 

Il tutorial mostra che ci sono un paio di modi per recuperare i dati dalla risorsa usando la chiamata get . Il primo metodo è passare un callback alla funzione get. Il callback verrà chiamato con il risultato dopo che la richiesta ajax ha restituito:

 $scope.twitter.get(function(result) { console.log('This was the result:', result); }); 

Capisco questo metodo. Ha perfettamente senso per me. La risorsa rappresenta un luogo sul web in cui è ansible ottenere dati, e get semplicemente effettua una chiamata ajax a un URL, ottiene json indietro e chiama la funzione di callback con il json. Il result param è quel json.

Ha senso per me perché sembra ovvio che si tratta di una chiamata asincrona. Cioè, sotto la cappa, la chiamata ajax si triggers e il codice che segue la chiamata non viene bloccato, continua a essere eseguito. Quindi, in un punto indeterminato in seguito, quando xhr ha esito positivo, viene richiamata la funzione di callback.

Quindi il tutorial mostra un metodo diverso che sembra molto più semplice, ma non capisco come funzioni:

 $scope.twitterResult = $scope.twitter.get(); 

Suppongo che l’xhr sottostante get debba essere asincrono, tuttavia in questa riga stiamo assegnando il valore di ritorno della chiamata get a una variabile, come se fosse restituito in modo sincrono.

Ho sbagliato a non capire questo? Come è ansible? Penso che sia davvero pulito che funzioni, semplicemente non capisco.

Capisco che get possa restituire qualcosa mentre l’xhr sotto di esso si spegne ed elabora in modo asincrono, ma se segui l’esempio di codice da te stesso, vedrai che $scope.twitterResult ottiene il vero contenuto di Twitter prima che vengano eseguite le righe successive. Ad esempio, se scrivi console.log($scope.twitterResult) immediatamente dopo tale riga, vedrai i risultati di twitter registrati nella console, non un valore temporaneo che verrà sostituito in seguito.

Ancora più importante, perché è ansible, come posso scrivere un servizio Angolare che sfrutti questa stessa funzionalità? Oltre alle richieste Ajax, ci sono altri tipi di archivi di dati che richiedono chiamate asincrone che possono essere utilizzati in JavaScript, che mi piacerebbe poter scrivere codice in modo sincrono in questo stile. Ad esempio, IndexedDB. Se potessi capire come le risorse integrate di Angular lo stanno facendo, farei un tentativo.

$ risorsa non è sincrona anche se questa syntax potrebbe suggerire che sia:

 $scope.twitterResult = $scope.twitter.get(); 

Quello che sta succedendo qui è che la chiamata a AngularJS, dopo la chiamata a twitter.get() , ritornerà immediatamente con il risultato che è una matrice vuota. Quindi, quando la chiamata asincrona è terminata e i dati reali arrivano dal server, l’array verrà aggiornato con i dati . AngularJS manterrà semplicemente un riferimento a un array restituito e lo riempirà quando i dati saranno disponibili.

Ecco il frammento di implementazione di risorse $ in cui avviene la “magia”: https://github.com/angular/angular.js/blob/master/src/ngResource/resource.js#L372

Questo è descritto anche nella documentazione delle risorse $ :

È importante rendersi conto che invocare un metodo $ risorsa object restituisce immediatamente un riferimento vuoto (object o matrice a seconda di isArray ). Una volta che i dati vengono restituiti dal server, il riferimento esistente viene popolato con i dati effettivi. Questo è un trucco utile poiché di solito la risorsa viene assegnata a un modello che viene quindi reso dalla vista. Avere un object vuoto non comporta alcun rendering, una volta che i dati arrivano dal server, l’object viene popolato con i dati e la vista si ricompone automaticamente mostrando i nuovi dati. Ciò significa che nella maggior parte dei casi non si deve mai scrivere una funzione di callback per i metodi di azione.

$ q può fare anche questo trucco. Puoi convertire un object normale in un “valore ritardato” usando qualcosa di simile a questo:

 var delayedValue = function($scope, deferred, value) { setTimeout(function() { $scope.$apply(function () { deferred.resolve(value); }); }, 1000); return deferred.promise; }; 

e poi usarlo in un controller, per ottenere un effetto simile a ciò che $ scope.twitter.get () fa nell’esempio dell’OP

 angular.module('someApp', []) .controller('someController', ['$scope', '$q', function($scope, $q) { var deferred = $q.defer(); $scope.numbers = delayedValue($scope, deferred, ['some', 'numbers']); }]);