Résolution synchrone de la promesse (bluebird vs. jQuery)
J'ai développé une petite lib pour le Dynamics CRM REPOS/ODATA webservice (CrmRestKit). La lib dependes sur jQuery et utilise la promesse d'un motif, repectivly la promesse comme motif de jQuery.
Maintenant, j'aime le port de cette lib pour bluebird et retirer le jQuery de dépendance. Mais je suis confronté à un problème car bluebird ne prend pas en charge le synchrone de la résolution de la promesse-objets.
Certaines informations de contexte:
L'API de la CrmRestKit si l'on excepte un paramètre facultatif qui définit si le web-appel de service doivent être effectuées dans la synchronisation ou la mode asynchrone:
CrmRestKit.Create( 'Account', { Name: "foobar" }, false ).then( function ( data ) {
....
} );
Lorsque vous passez "vrai" ou omettre le dernier paramètre, la méthode créée le dossier à synchroniser. mode.
Parfois, il est nécessaire d'effectuer une opération de synchronisation en mode, par exemple, vous pouvez écrire du code JavaScript pour Dynamics CRM est involed pour le sauver-cas d'une forme et dans ce gestionnaire d'événements, vous devez effectuer la synchronisation pour la validation (par exemple, de valider qu'un certain nombre de documents existent, dans le cas où le nombre de documents existent, annuler l'enregistrement d'opération, et afficher un message d'erreur).
Mon problème est maintenant le suivant: bluebird ne prend pas en charge la résolution dans sync-mode. Par exemple quand je fais la suite, la "puis" gestionnaire est appelé en mode asynchrone:
function print( text ){
console.log( 'print -> %s', text );
return text;
}
///
///'Promise.cast' cast the given value to a trusted promise.
///
function getSomeTextSimpleCast( opt_text ){
var text = opt_text || 'Some fancy text-value';
return Promise.cast( text );
}
getSomeTextSimpleCast('first').then(print);
print('second');
Le résultat est le suivant:
print -> second
print -> first
Je m'attends à ce que le "second" s'affiche après la "première" car la promesse est déjà résolu avec une valeur. Donc, je suppose que puis-gestionnaire d'événements est immédiatement appelée lorsqu'il est appliqué sur une question déjà résolue promesse-objet.
Quand je fais la même chose (utiliser alors déjà résolu promesse) avec jQuery, je vais avoir mon résultat attendu:
function jQueryResolved( opt_text ){
var text = opt_text || 'jQuery-Test Value',
dfd = new $.Deferred();
dfd.resolve(text);
//return an already resolved promise
return dfd.promise();
}
jQueryResolved('third').then(print);
print('fourth');
Cela génère la sortie suivante:
print -> third
print -> fourth
Est-il un moyen de faire bluebird travailler de la même façon?
Mise à jour:
Le code fourni est juste pour illustrer le problème. L'idée de la lib est: Indépendamment de l'exécution en mode sync, async) l'appelant sera toujours face à une promesse-objet.
Concernant "... la demande de l'utilisateur... ne semble avoir aucun sens": Lorsque vous fournissez des deux méthodes "CreateAsync" et "CreateSync" il est également à l'utilisateur de décider de la façon dont l'opération est exécutée.
De toute façon avec la mise en œuvre actuelle le comportement par défaut (dernier paramètre est facultatif) est une exécution asynchrone. Donc 99% du code exige une promesse-objet, le paramètre facultatif est à utiliser uniquement pour le 1% des cas où vous avez simplement besoin d'une synchronisation de l'exécution. En outre, j'ai développé de lib pour moi-même et je l'utilise dans 99,9999% des cas, le mode asynchrone, mais je pensais que c'est agréable d'avoir la possibilité d'aller à la synchronisation de la route que vous le souhaitez.
Mais je pense que je suis au point une méthode de synchronisation doit simplement retourner la valeur. Pour la prochaine version (3.0) je vais mettre en œuvre "CreateSync" et "CreateAsync".
Merci pour vos commentaires.
Mise à jour-2
Mon intension pour le paramètre facultatif est d'assurer un consistend comportement ET prévenir erreur de logique. Supposons que votre en tant que consommateur de ma methode "GetCurrentUserRoles" qui utilise une lib. Donc, la méthode sera toujours retourner une promesse, cela signifie que vous devez utiliser le "puis" la méthode à exécuter du code qui dépend du résultat. Ainsi, lorsque certaines écrit ce code, je suis d'accord c'est totalement faux:
var currentUserRoels = null;
GetCurrentUserRoles().then(function(roles){
currentUserRoels = roles;
});
if( currentUserRoels.indexOf('foobar') === -1 ){
//...
}
Je suis d'accord que ce code va casser lorsque la méthode "GetCurrentUserRoles" changements de synchronisation async.
Mais je comprends que ce que je pas un bon design, parce que le consommateur doit maintenant qu'il traite avec une méthode asynchrone.
source d'informationauteur thuld
Vous devez vous connecter pour publier un commentaire.
Version courte: je comprends pourquoi vous voulez le faire, mais la réponse est non.
Je pense que la question posée est de savoir si un formulaire de promesse doit immédiatement lancer un rappel, si la promesse est déjà terminée. Je ne peux penser à beaucoup de raisons pour lesquelles cela pourrait se produire, par exemple, une asynchrones procédure d'enregistrement qui enregistre uniquement des données si des modifications ont été apportées. Il peut être capable de détecter les changements à partir du côté client en mode synchrone de la mode sans avoir à passer par une ressource externe, mais si des modifications sont détectées alors, et seulement alors, serait une opération asynchrone être nécessaire.
Dans d'autres environnements qui ont des appels asynchrones, le motif semble être que le développeur est responsable de comprendre que leur travail peut effectuer immédiatement (par exemple, .NET cadre de la mise en œuvre de la async motif accueille ce). Ce n'est pas un problème de conception du cadre, c'est la façon dont il est mis en œuvre.
JavaScript développeurs (et de nombreux commentateurs ci-dessus) semblent avoir un point de vue différent sur ce, insistant sur le fait que si quelque chose peut être asynchrone, il doit toujours être asynchrone. Si c'est "juste" ou non n'a aucune importance - selon les spécifications que j'ai trouvé à https://promisesaplus.com/point 2.2.4 les états qui, en gros, aucun rappel n'est peut être appelé jusqu'à ce que vous êtes en dehors de ce que j'appellerai "script code" ou "code d'utilisateur"; c'est, la spécification indique clairement que, même si la promesse est terminée, vous ne pouvez pas appeler la fonction de rappel immédiatement. J'ai vérifié un certain nombre d'autres endroits, et ils ne disent rien sur le sujet ou d'accord avec la source d'origine. Je ne sais pas si https://promisesaplus.com/ pourrait être considéré comme une source majeure d'informations à ce sujet, mais pas d'autres sources que j'ai vu en désaccord avec elle, et il semble être le plus complet.
Cette limitation est quelque peu arbitraire et franchement, je préfère le .NET point de vue sur celui-ci. Je vais laisser à d'autres de décider si elles le jugent "mauvais code" pour faire quelque chose qui pourrait ou ne pourrait pas être synchrone d'une manière qui ressemble asynchrone.
Votre question réelle est de savoir si ou de ne pas Bluebird peut être configuré pour effectuer la non-JavaScript comportement. En terme de Performance il y a peut être un léger avantage à le faire, et en JavaScript, tout est possible si vous essayez assez dur, mais comme la Promesse de l'objet devient de plus en plus omniprésent à travers les plates-formes, vous verrez un changement de l'utiliser comme un composant natif au lieu de la coutume écrite polyfills ou des bibliothèques. En tant que tel, quelle que soit la réponse est aujourd'hui, à retravailler une promesse de Bluebird est susceptible de vous causer des problèmes à l'avenir, et que votre code ne devrait probablement pas être écrite à dépendre sur ou de fournir une résolution immédiate de une promesse.
Le point de promesses est de faire asynchrone code plus facile, c'est à dire plus proche de ce que vous ressentez lors de l'utilisation de synchrone code.
Vous êtes à l'aide du code synchrone. Ne pas rendre les choses plus compliquées.
Et qui devrait être la fin de celui-ci.
Si vous voulez garder la même interface asynchrone même si votre code est synchrone, alors vous devez faire tout le chemin.
then
obtient votre code de le flux d'exécution normale, parce que c'est censé être asynchrone. Bluebird t-il le droit chemin. Une explication simple de ce qu'il fait:Noter que bluebird n'est pas vraiment le faire, c'est juste pour vous donner un exemple simple.
Essayer!
Cela permettra de sortie suivants:
Vous pourriez penser que c'est un problème, car il n'y a pas moyen d'avoir
et d'avoir
getSomeText
"first"
imprimé avant"second"
lorsque la résolution est synchrone.Mais je pense que vous avez un problème de logique.
Si votre
getSomeText
fonction peut être synchrone ou asynchrone, selon le contexte, alors il ne devrait pas avoir d'impact de l'ordre de l'exécution. Vous utilisez des promesses pour s'assurer qu'il est toujours le même. Avoir une variable à l'ordre de l'exécution risquerait de devenir un bug dans votre application.Utilisation
Dans les deux cas (synchrone avec
cast
ou asynchrone résolution), vous aurez la bonne exécution de la commande.Remarque que le fait d'avoir une fonction étant parfois synchrones et parfois pas n'est pas un de bizarre ou de rare cas (pensez à propos de cache de la manipulation, ou de mise en commun). Vous avez juste à supposer qu'il en est asynchrone, et tout sera bien.
Mais de demander à l'utilisateur de l'API de précis avec un argument booléen si il veut de l'opération asynchrone ne semble avoir aucun sens si vous ne laissez pas le royaume de JavaScript (c'est à dire si vous n'utilisez pas de code natif).
Il y a quelques bonnes réponses déjà ici, mais pour résumer l'essentiel de la question de façon très succincte:
Avoir une promesse (ou d'autres API asynchrone) qui est parfois asynchrone et parfois synchrone est une mauvaise chose.
Vous pouvez penser que c'est bien parce que l'appel initial à votre API prend une valeur de type boolean pour basculer entre sync/async. Mais si c'est enterré dans certains wrapper code et la personne à l'aide de que code ne sais pas à propos de ces manigances? Ils ont juste retrouvé avec certains unpreditable comportement sans aucune faute de leur propre.
La ligne du bas: Ne pas essayer de le faire. Si vous voulez comportement synchrone, ne retourne pas une promesse.
Sur ce, je vous laisse avec cette citation de Vous Ne Savez pas JS:
Qu'en ce cas, également, CrmFetchKit connexes qui, dans la dernière version utilise Bluebird. J'ai mis à jour depuis la version 1.9, qui était basé sur jQuery. Toujours le vieux code de l'application qui utilise CrmFetchKit a des méthodes dont les prototypes que je ne peut pas ou ne change pas.
Application Existante Code
Vieux CrmFetchKit de mise en œuvre (une version personnalisée de fetch())
Nouveau CrmFetchKit mise en œuvre
Mon problème est que l'ancienne version avait la dfd.résoudre(...) où j'ai été capable de passer n'importe quel nombre de paramètres dont j'ai besoin.
La nouvelle de la mise en œuvre renvoie simplement, le parent semble appeler la fonction de rappel, je ne peux pas l'appeler directement.
Je suis allé et a fait une version personnalisée du fetch() dans la nouvelle mise en œuvre
Mais le problème est que la fonction de rappel est appelée deux fois, une fois quand je le fais de façon explicite et la seconde par le cadre, mais il passe d'un seul paramètre. De le tromper et de "dire" de ne pas appeler de n'importe quoi parce que je ne explicitement j'essaie de les appeler .annuler (), mais il est ignoré. J'ai compris pourquoi, mais encore comment tu fais le "dfd.résoudre(le résultat.entités, le résultat.totalRecordCount);" dans la nouvelle version, sans avoir à modifications des prototypes dans le app qui utilise cette bibliothèque ?
Vous pouvez en fait faire cela, oui.
Modifier le
bluebird.js
fichier (pour les mnp:node_modules/bluebird/js/release/bluebird.js
), avec la modification suivante:Pour plus d'infos, voir ici: https://github.com/stacktracejs/stacktrace.js/issues/188