lunes, 8 de septiembre de 2014

Using promises to simplify javascript asynchronous development in SharePoint (II)

Some weeks ago I published here an introduction on how to use promises to simplify javascript asynchronous development. Today I want to give some more information about this topic just to highlight one of the first challenges you are going to face when using this approach: executing something only after a list of parallel tasks have been completed.

Imagine you want to copy all the elements from one list to another and when everything has been done, notify the user somehow.

First, using promises one would end up building a method like the one below to retrieve all elements from a given list.

var getListItemsDeferred = function (listName) {
	var deferred = $.Deferred();

    var context = SP.ClientContext.get_current();
    var list = context.get_web().get_lists().getByTitle(listName);
        
    var camlQuery = new SP.CamlQuery();
    camlQuery.set_viewXml(
        '<View>' +
        	'<Query>' +
        		'<Where>' +
        			'<Geq>' + 
        				'<FieldRef Name=\'ID\'/>' + 
				        '<Value Type=\'Number\'>1</Value>' +
				    '</Geq>' +
				 '</Where>' +
			'</Query>' + 
	        '<RowLimit>10</RowLimit>' +
	     '</View>'
    );

    var items = list.getItems(camlQuery);
        
    context.load(items);

    var o = { deferred:deferred , items:items };
    context.executeQueryAsync(
    	Function.createDelegate(o, this.getListItemsDeferredCallback),
		Function.createDelegate(o, this.failCallback));

	return deferred.promise();
}

And then would write some callback methods like the ones below.

var getListItemsDeferredCallback = function () {
    this.deferred.resolve(this.items);
}

var failCallback = function (error, args) {
    this.deferred.reject(args.get_message());
}

Then, using the same technique, we would be creating a method like the one below to create an item in another given list:

var createListItemsDeferred = function(listName, title) {
	var deferred = $.Deferred();

    var context = SP.ClientContext.get_current();
    var list = context.get_web().get_lists().getByTitle(listName);
        
	var itemCreateInfo = new SP.ListItemCreationInformation();
	var item = list.addItem(itemCreateInfo);
	item.set_item('Title', title);
	item.update();
	
    context.load(item);

    var o = { deferred:deferred, item:item };
    context.executeQueryAsync(
    	Function.createDelegate(o, this.createListItemsDeferredCallback),
		Function.createDelegate(o, this.failCallback));

	return deferred.promise();
}

And, again, some callback methods.

var createListItemsDeferredCallback = function () {
    this.deferred.resolve(this.item);
}

Finally, to make everything work we would create a main method similar to the one below

function copyItemsWithPromises(sourceListTitle, destinationListTitle) {

	var p = getListItemsDeferred(sourceListTitle);
	
	p.done(function(items) {
		var itemsEnumerator = items.getEnumerator();
		while (itemsEnumerator.moveNext()) {
	
	        var item = itemsEnumerator.get_current();
	        var title = item.get_item('Title');
	        
	        var p2 = createListItemsDeferred(destinationListTitle, title);
	        
	        p2.done(function(item) {
	        	alert("item with title: " + item.get_item("Title") + " created");
	        });
	        
	        p2.fail(function(err) {
	        	alert(err);
	        });
        }

	});
	
	p.fail(function(err) {
		alert(err);
	});
}

The method above will read the items from a list and will create them all in a second list. An alert will be shown to the user every time a new item is created but, how could I continue with the process I need to implement only when all items have been created?

One possible answer is described in the following listing.

function copyItemsWithPromisesAndThenDoSomething(sourceListTitle, destinationListTitle) {

	var p = getListItemsDeferred(sourceListTitle);
	
	p.done(function(items) {
		var itemsEnumerator = items.getEnumerator();

		var p_array = [];

		while (itemsEnumerator.moveNext()) {
	
	        var item = itemsEnumerator.get_current();
	        var title = item.get_item('Title');
	        	        
	        var p2 = createListItemsDeferred(destinationListTitle, title);
	        p2.then(function() {
	        	// Do whatever you want here...
	        });
	        
			p_array.push(p2);
        }

		$.when.apply($, p2).then(function () {
    		alert("I have created all elements!!");
    	});

	});
	
	p.fail(function(err) {
		alert(err);
	});
}

0 comentarios: