asynchronous code handling
(and how to test that 😁)

UI5con 2017 - Volker Buzek

j&s-soft GmbH

SAP-Consultancy with focus technology & innovation

Big Pic

Out there...

var oCurrencyModel = new sap.ui.model.json.JSONModel();
oCurrencyModel.loadData(
    "/some/API/endpoint",
    null,
    false
);
var sCurrency = oCurrencyModel.getData();
$.ajax({
    type: 'GET',
    url: "/some/API/endpoint",
    async: false
})

CORS demysitifed

Sequential

aCars.forEach(function (oCar) {
   var oModel = new JSONModel();
   oModel.loadData(
       "/whatever/REST/endpoint",
       null, // no addtl params
       false // sync!
   );
   oCar.drive();
}

Asynchronous (1)

var aRequestTimes = [], iCars = aCars.length;
aCars.forEach(function(oCar, iIndex) {
	var oModel = new JSONModel();

	oModel.attachRequestCompleted(function() {
		aRequestTimes.push(
			this.getData().delay
		);

		if (iIndex == (iCars - 1)) {
			_asyncFinished();
		}
	});

	oModel.loadData("/whatever/REST/endpoint");
});

Asynchronous (2)

function _asyncFinished() {
	var iTotalDelay = aRequestTimes.reduce(function(iTotal, iDelay) {
		return iTotal + iDelay;
	});

	aRequestTimes.map(function(iDelay, iIndex) {
		aCars[iIndex].drive();
	);
}

Asynchronous - issue

1 3
2 2
3 5
4 1
5 4

Promises (1)

wrap the original callback-based function



var oModel = new JSONModel();
return new Promise(function(fnResolve, fnReject) {
	oModel.attachRequestCompleted(function() {
		fnResolve(this.getData()) //contains unique key
	});
	oModel.attachRequestFailed(function(oErr) {
		fnReject(oErr);
	});
	oModel.loadData("/whatever/REST/endpoint");
})

Promises (2)

use wrapper in Promise.all()


return Promise.all(
	aCars.map(function(oCar) {
		var oModel = new JSONModel();
		return _promisifiedLoad(oModel, oCar.getColor());
	})
)
            

Promises (3)

trigger Promise-chain with Promise.all()


_promiseAll()
	.then(function(aResolvedCalls) {
		aResolvedCalls.forEach(function(oResult) {
			getCar(oResult.color).drive();
		});
		return true; // keep the chain going
	})
	.then(function() {
		// do sth else and/or
		// return a value to keep chain going
	})
	.catch(function(oErr) {
		console.log(JSON.stringify(oErr));
	})
            

Promises (4)

Bonus: Promise.race()


Promise.race(
	aCars.map(function(oCar) {
		var oModel = new JSONModel();
		return _promisifiedLoad(oModel, oCar.getColor());
	})
)
            

Summary

Pro Con
Sync sequential coding blocking
Async non-blocking more dev effort,
difficult sequence handling
Promises non-blocking,
sequence handling
hard to debug

Testing Promises (1)

test the chain definition


(typeof oPromise.then === "function"
&& typeof oPromise.catch === "function")

Testing Promises (2)

test the behaviour

defintion:



var oModel = new JSONModel();
return new Promise(function(fnResolve, fnReject) {
	oModel.attachRequestCompleted(function() {
		fnResolve(this.getData()) //contains unique key
	});
	oModel.attachRequestFailed(function(oErr) {
		fnReject(oErr);
	});
    oModel.loadData("/whatever/REST/endpoint");
})

force resolving


sinon.stub(oModel, "loadData", function () {
    oModel.fireRequestCompleted()
})
// ...
oPromise()
    .then(function() {
        assert.ok(true, "reached the .then-state");
        oModel.loadData.restore();
    })
                

force rejection


sinon.stub(oModel, "loadData", function () {
    oModel.fireRequestFailed()
})
// ...
oPromise()
    .catch(function() {
        assert.ok(true, "reached the .catch-state");
        oModel.loadData.restore();
    })
                

Testing Promises (3)

test the Promise-chain

assumption:


var oPromiseContainer = {
	initPromiseChain: function() {
		return oPromise;
	}
}
                

step the Promise chain


var sMsg = "resolved Promise!";
sinon.stub(oPromiseContainer, "initPromiseChain")
	.returns(Promise.resolve(sMsg));
// ...
oPromiseContainer.initPromiseChain()
	.then(function(oResponse) {
		assert.strictEqual(oResponse, sMsg, "Promise resolved correctly!");
		oPromiseContainer.initPromiseChain.restore();
	});
                

break the Promise chain


var oError = new Error("meeeh :(");
sinon.stub(oPromiseContainer, "initPromiseChain")
	.returns(Promise.reject(oError));
// ...
oPromiseContainer.initPromiseChain()
	.catch(function(oPromiseErr) {
		assert.strictEqual(oError, oPromiseErr, "Promise rejected correctly!");
		that.oPromiseContainer.initPromiseChain.restore();
	});
                

Volker Buzek
j&s-soft GmbH

SAP Development Architect Mobility

W: www.js-soft.com
T: @vobu
E: volker.buzek@js-soft.com
M +49.151.649.622.50