Recently while trying out the new SharePoint Framework in conjunction with the Sp-PnP-Js-Core library we came across a small difficulty dealing with the Promises while accessing the SharePoint API.

javascript-promise

Our intent was to develop a simple web part to manage lists on SharePoint using the Sp-PnP-Js library as way of comunication with the Sharepoint. For the most part it was a simple and straight foward process but when we needed to create something a little bit more complex we ended up having to chain multiple Promises making the block of code big and hard to read and understand. So we set out to find a solution to simplefying our Promise chains.

Our “real world” problem was…

We wanted to programatically add fields to a sharepoint list and then those fields to the lists default view.

So to do that we would execute a block of code similiar to this:

import pnp,{FieldAddResult} from 'sp-pnp-js'; 

public createField(xmlQuery: string, listId: string) {
    pnp.sp.web.lists.getById(listId).defaultView.get().then((view: any) => {
        pnp.sp.web.lists.getById(listId).fields.createFieldAsXml(xmlQuery).then((field: FieldAddResult) => {
            pnp.sp.web.lists.getById(listId).getView(view.Id).fields.add(field.data.InternalName);
        });

    });
}

The promise chain above is simple and uses only three promises. Although it is not simple it is relatively easy to read and understand. But what if we wanted to add five or tens fields with one call to this method?  We would have to chain it all and it would become a mess.

So we tried creating a batch (from the sp-pnp-js library) of promises, add all the promises to that batch and then execute it but this still created a big chunck of code but at least it was simpler to read and understand.

Not satisfied with the solutions available we decided to make our own.

Our solution was…

import pnp, { List, FieldAddResult } from 'sp-pnp-js';
 
class Example {
 
    public createField(xmlQuery: string, listId: string): Promise<void> {
        let list: List = pnp.sp.web.lists.getById(listId);
        let fieldResultPromise: Promise<FieldAddResult> = list.fields.createFieldAsXml(xmlQuery);
        let defaultViewPromise: Promise<any> = list.defaultView.get();
 
        return Promise.all([defaultViewPromise, fieldResultPromise])
                      .then((results: [any, FieldAddResult]) => {
                          list.getView(results[0].Id).fields.add(results[1].data.InternalName);
                      });
    }
 
    public createFields(xmlQueries: string[], listId):Promise<void[]> {
        let createFieldsPromises = xmlQueries.map((query: string, index: number) => { this.createField(query, listId); });
        return Promise.all(createFieldsPromises);
    }
}

In our solution we reorganized the code to add one field making it easier to read. Then we created a new method (createFields) that mapped an array of queries to the a call of the createField method  therefore creating an array of promises that is executed using the Promise.All method to execute them all.


0 Comments

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.