From f03cbebaa1039594af903400ea0b27f98f4d15cd Mon Sep 17 00:00:00 2001 From: Vladimir Kosmala Date: Tue, 30 Jan 2018 17:43:08 +0100 Subject: [PATCH] Added guide on async calls --- README.md | 2 +- docs/guides/async-calls.md | 109 +++++++++++++++++++++++++++++++++ docs/guides/fetch-websocket.md | 2 - 3 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 docs/guides/async-calls.md delete mode 100644 docs/guides/fetch-websocket.md diff --git a/README.md b/README.md index e7cf177..cf1a8e5 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Create mini applications step by step with explanations. ### Guides -- [Async calls with fetch (ajax) and websocket](./docs/guides/fetch-websocket.md) +- [Async calls (ajax)](./docs/guides/fetch-websocket.md) - [Scale the code of your application (architecture)](./docs/guides/scale-app.md) - [From imperative to reactive programming (jQuery to React)](./docs/guides/reactive-programming.md) diff --git a/docs/guides/async-calls.md b/docs/guides/async-calls.md new file mode 100644 index 0000000..e58febd --- /dev/null +++ b/docs/guides/async-calls.md @@ -0,0 +1,109 @@ +# Guide - Async calls (ajax) + +Javascript deals well with asynchronous calls like network requests. Historically it was implemented though callback functions like this. + +```js +request(arguments, function callback(error, result) { + // do something with result +}); +``` + +But this leads to spaghetti code and a more formal way was created: Promises. + +```js +var resultPromise = request(arguments); +resultPromise.then(function callback(result) { + +}); +resultPromise.catch(function callback(error) { + +}); +``` + +Notice that the callbacks are now binded to an object, the promise. + +Then Javascript went full asynchronous is the langage itself with await. + +```js +try { + var resultPromise = await request(arguments); +} catch (error) { + +} +``` + +We can integrate promises with an observable model: + +```js +class Model extends Observable { + fetchImages() { + return fetch('/api/images') + .then((images) => this.setImages(images)) + .catch((error) => this.handleErrors(error)); + } + + setImages(images) { + this.images = images; + this.notify(); + } + + handleErrors(error) { + this.error = error; + this.notify(); + } +} +``` + +The method fetchImages will create a network request and return a promise. When the promise is finished it will either call the success method or a generic handler. + +Because you often have a lot of requests, it is a efficient to have a generic handler for errors which will for example print an error message to the user. + +fetchImages also returns a promise, the caller could use it to know that the call is finished or not and avoid calling again while the request is still in progress. + +Of course we call `notify()` each time the model changes, for both `then` and `catch` callbacks. + +Here is a practical example where we don't want the user to request again when a request has been made: + +```js +class Model extends Observable { + fetchImages() { + if (this.fetchingImages) { + return; + } + + this.fetchingImages = true; + this.notify(); + + return fetch('/api/images') + .then((images) => { + this.setImages(images); + }) + .catch((error) => { + this.handleErrors(error); + }) + .finally((error) => { + this.fetchingImages = false; + this.notify(); + }); + } + + setImages(images) { + this.images = images; + this.notify(); + } + + handleErrors(error) { + this.error = error; + this.notify(); + } +} + +function button(model) { + const action = model.fetchingImages ? e => model.fetchImages() : null; + const className = model.fetchingImages ? 'disabled' : ''; + return h('button', {onclick: action, class: className}, 'Fetch images') +} +``` + +We used the method `finally` of the promise to clean up the request. + diff --git a/docs/guides/fetch-websocket.md b/docs/guides/fetch-websocket.md deleted file mode 100644 index 8e663cb..0000000 --- a/docs/guides/fetch-websocket.md +++ /dev/null @@ -1,2 +0,0 @@ -# Guide -