2015-09-25 2 views
4

Je travaille avec le nouveau office.js. J'utilise la fonctionnalité Excel.run qui renvoie une promesse. J'ai une question sur le modèle de promesses mis en œuvre par la bibliothèque.Enchaînement des promesses dans Office.js avec Excel.run()

Les échantillons montrent tout ce modèle

Excel.run(function (ctx) { 

    //set up something 

    return ctx.sync().then (function() { 
    //call another function somewhere to chain operations 
    }); 

}).then (function() { 
    //do something else if you want 
}).catch (function (error) { 
    handle errors 
}); 

Le problème est le ctx.sync(). Puis() contenu dans Excel.run() La façon dont il est présenté, vous ne pouvez pas les promesses de la chaîne conformément aux promesses spec car vous perdez l'objet contexte si vous essayez de gérer le then() en dehors de Excel.run() Ainsi, le motif semble promouvoir les appels de fonctions imbriquées, ce que les promesses sont censées éliminer.

Ce que je veux faire est séquence plusieurs appels en même temps par CHAÎNAGE comme ceci:

Excel.run(function (ctx) { 
    return ctx.sync(); 
}).then (function (ctx) { 
    return ctx.sync(); 
}).then (function (ctx) { 
    return ctx.sync(); 
}).then (function (ctx) { 
    return ctx.sync(); 
}).catch (function (error) { 

}); 

Est-ce possible?

Répondre

1

Bien que cela serait possible puisque Excel.RequestContext.sync prend une valeur de passage, l'objectif de Excel.run est de gérer trackedObjects pour la fonction qu'il se passe. Dans les promesses enchaînées après Excel.Run vous devez gérer vous-même trackedObjects, par conséquent vaincre le but de Excel.Run.

Je suggérerais de déclarer votre fonction en dehors du Excel.Run si vous n'aimez pas l'indentation ajoutée, ou en créant votre propre objet RequestContext.

9

En général, le but de Excel.run est pour une opération séquentielle contre l'OM avec un nettoyage automatique à la fin. Autrement dit, Excel.run crée un contexte, exécute votre opération, puis nettoie tous les objets hôtes qui ont été alloués. Ceci étant dit, comme mentionné par Gab Royer, pouvez passer des objets. De plus, chaque objet Excel possède un back-pointeur vers son "contexte" via la propriété ".context". Ainsi, par exemple, vous pouvez le faire:

Excel.run(function (ctx) { 
    var worksheet = ctx.workbook.worksheets.getActiveWorksheet(); 
    return ctx.sync(worksheet); 
}).then(function(worksheet) { 
    worksheet.name = "Test" 
    return worksheet.context.sync(); 
}).catch(function(e) { 
    console.log(e) 
}); 

Comme vous pouvez le voir, dans le code ci-dessus, vous avez créé l'objet de feuille de calcul à l'intérieur du Excel.run, mais que vous utilisez à l'extérieur.

Si vous avez quelque chose comme un objet Range, cela devient un peu plus compliqué. Les plages, à la différence des feuilles de calcul, ne possèdent pas d'ID persistants (comment pourraient-ils? Il existe essentiellement un nombre incalculable de permutations de toutes les combinaisons de cellules possibles). Au lieu de cela, pendant Excel.run, nous créons automatiquement des pointeurs persistants vers les objets Range de sauvegarde qui sont ajustés et conservés par Excel. Lorsque le lot à l'intérieur de Excel.run se termine, nous disons à l'hôte de détruire ces références. Donc, si vous aviez le code comme ceci:

Excel.run(function (ctx) { 
    var range = ctx.workbook.getSelectedRange(); 
    return ctx.sync(range); 
}).then(function(range) { 
    range.format.fill.color = "red"; 
    return ctx.sync(); 
}).catch(function(e) { 
    console.log(e) 
}) 

Il se heurterait une erreur « InvalidObjectPath ». Toutefois, vous pouvez désactiver le nettoyage de l'objet suivi en ajoutant manuellement l'objet à la collection ctx.trackedObjects. En faisant cela, cependant, vous prenez sur vous de nettoyer à la fin - et vous devez être très prudent, de ne pas oublier de nettoyer non seulement sur le succès, mais sur l'échec. Sinon, vous créez essentiellement une fuite de mémoire qui continuera à ralentir l'application hôte Excel.

var range; 
Excel.run(function (ctx) { 
    range = ctx.workbook.getSelectedRange(); 
    ctx.trackedObjects.add(range); 
    return ctx.sync(range); 
}).then(function(range) { 
    range.format.fill.color = "red"; 
    return range.context.sync(); 
}).then(function() { 
    // Attempt to clean up any orphaned references 
    range.context.trackedObjects.remove(range); 
    range.context.sync(); // don't need to await it, since it's just the final cleanup call 
}).catch(function(e) { 
    console.log(e); 
}) 

Longue histoire courte: il est certainement faisable, et vous pouvez utiliser des objets après Excel.run. Vous aurez juste besoin d'être responsable de la gestion de la mémoire pour tous les objets qui nécessitent un "suivi". Dans l'exemple ci-dessus, il n'y a pas de raison de passer par cet effort, puisque vous auriez pu avoir le même code dans Excel.run (rappelez-vous, vous pouvez enchaîner les promesses dans le lot à l'intérieur de Excel.run aussi, pas besoin de le faire à l'extérieur). Mais si vous avez un scénario, où, disons, vous avez un travail du minuteur qui doit être exécuté de temps en temps (par exemple, pour mettre à jour un ticker), ou vous voulez créer un bouton avec un gestionnaire onclick pour un objet particulier, etc. La technique ci-dessus vous permettra de créer les objets dans Excel.run, puis de les utiliser en dehors de celui-ci.

PS: En ce qui concerne le modèle nécessitant imbrication: Il est vrai que si vous avez besoin de la chaîne ctx.sync() appels dans Excel.run, vous finirez avec une couche d'imbrication - mais un seul supplémentaire couche. À l'intérieur, vous seriez toujours en mesure d'enchaîner vos promesses sans la pyramide de rappel. E.g.,:

Excel.run(function (ctx) { 
    var range = ctx.workbook.worksheets.getActiveWorksheet().getRange("A1:C3"); 
    range.load("values"); 
    return ctx.sync() 
     .then(function() { 
      // Some set of actions against the OM, now that the "values" 
      // property has been loaded and can be read from the "range" object. 
     }) 
     .then(ctx.sync) 
     .then(function() { 
      // Another set of actions against the OM, presumably after doing 
      // another load-requiring operation (otherwise could have 
      // been part of the same .then as above) 
     }) 
     .then(ctx.sync) 
     .then(function() { 
      // One final set of actions 
     });  
}).catch(function(error) { 
    console.log("Error: " + error); 
}); 
+3

Ceci devrait être copié directement dans la documentation. Excellent aperçu. Merci! – user3653075

+0

Donc vous avez mentionné ceci: "vous voulez créer un bouton avec un gestionnaire onclick pour un objet particulier, etc. la technique ci-dessus vous permettra de créer les objets à l'intérieur de Excel.run, et ensuite les utiliser en dehors de celui-ci." Je fais exactement cela, mais je trouve que si j'appelle '.select()' sur mon corps d'objet, mon objet n'est pas * défilé vers ou mis en surbrillance, mais je ne reçois aucune exception. J'ai un document Word avec une table dedans. J'ajoute une référence à une cellule spécifique, l'ajoute en tant qu'objet suivi et attache un gestionnaire d'événement. Mais rien ne se passe sur le clic. Est-ce que cela a fonctionné pour vous? –

+0

Pour quiconque avait ce problème, je ne comprenais pas la nature de la synchronisation du contexte. En dehors de mon contexte initial, j'ai dû ouvrir un nouveau 'Word.run' pour obtenir un nouveau contexte, puis appeler' context.sync() 'sur mon réel' trackedObject' après avoir effectué 'select()'. Je pense que cela est assez bien illustré dans l'exemple 'setTimeOut', je l'ai juste raté. –