According MDN all three methods are very similar, and at the end they produce very similar result.
they all accept this object as first parameter.
then apply accepts and array of arguments vs bind and call accept a sequence of arguments.
Function.prototype.apply()
The apply() method calls a function with a given this value, and arguments provided as an array
Function.prototype.bind()
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called
– Let’s imagine that you have a card component that you could show and hide with a button.
– When the component become visible an asynchronous function is called to get current time.
– Once the fetch is done, it will update the card showing current time.
The problem
– Let’s say that this asynchronous function is taking a few seconds to bring data back, and before it finishes the user decides to `hide` (or more appropriate thing to use is unmount) the card by clicking the button.
– when fetch is done, the asynchronous function will call setState to update the component that is no longer there and you will receive the nasty warning telling you that React can’t perform a React state update on an unmounted component.
And it makes perfect sense since the component and it’s DOM is not there any more.
The solution
– Use React refs to determine if the component is mounted or not.
– Line 5 – we added mounted param which is a reference to any DOM object in this component. If the component is not mounted mounted.current will return null
– Line 26 – we just add the actually reference tag to any DOM object that is constantly visible in our component. In most cases this is just the card wrapping div.
– Line 14 – we just check if the component exist or not before performing stateUpdate
Obviously we could also use the old fashion DOM way and use querySelector to check if the element is actually there but I guess useRefs is the more “React way” of doing this.
Line 5 tells enginx to intercept all requests / and proxy them from http://localhost:3000
If the request from example is /home Nginx will make request to http://localhost:3000/home
If for example we want to proxy only specific urls (let’s say only *.php) we could do this:
A service worker API is relatively new technology, allowing for running a special type of web worker (a JavaScript), that can be installed in the browser and could provide special features to the website like caching data and allowing for offline load.
Service workers are also heavily used in progressive apps since they provide functionalities for caching resources and push notifications.
Before we jump into building an example app, let’s see what are the main service worker properties.
Service worker properties:
– Service workers are plain JavaScript APIs.
– Service workers run on separate thread so they can’t access the elements of the main page like the DOM elements but this is not the intend of how they are used.
– Service workers can listen on an events emitted from the browser and execute an appropriate script. In example a service worker could listen on browser fetch event and fetch the data or return it from cache.
Service worker lifecycle.
On a first page visit the only thing that happens is that service worker got installed. No code inside the service worker got executed, unless the user hit another page from this website or reloads the current page. Then service worker kicks in and could provide the extra functionality to the site.
The service intentionally delays the response 5 seconds so this way we could observe when we open the page for the first time how the response is delayed, since service worker is installing and don’t have the response cached. Then if we reload the page the service worker will intercept the request and will serve last cached response right away and then will make a request to the service and update the page.
Building a caching layer for the web resources using service worker.
Let’s create the page.
index.html
<html lang="en">
<head>
<meta charset="utf-8">
<title>title</title>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
My fancy new site !
<hr>
<div id="container"></div>
</body>
</html>
What’s happening here:
– line 1: we check if the browser supports service workers.
– line 2: added event listener, listening for load event and on load it installs the service worker. The rest is just error handling.
-line15:we make a request to the service using plain jQuery AJAX request.
style.css
body {
background: silver
}
Now it’s time to write the actual service worker.
sw.js
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'./index.html',
'./style.css',
'./script.js',
'https://www.toni-develops.com/external-files/examples/service-workers/delayed-response.php',
'https://code.jquery.com/jquery-3.3.1.min.js'
];
self.addEventListener('install', event => {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Opened cache');
cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', event => {
console.log("(9)served from service worker: ",event.request.url);
// serve as soon as possible from cache
event.respondWith(fromCache(event.request));
// update cache
event.waitUntil(
update(event.request)
);
});
/**
*
* Helper methods
*/
function fromCache(request) {
return caches.open(CACHE_NAME).then(cache => {
return cache.match(request);
});
}
function update(request) {
caches.open(CACHE_NAME).then( cache => {
fetch(request).then( response => {
cache.put(request, response)
});
});
}
What we just did:
– line 1: we specified the cache key (we have to make sure that it is unique)
– line 2: we created an array urlsToCache having all URLs that we want to cache. If one of the URLs fail, service worker will fail to install so we have to keep this url list short.
– line 11: added event listener, that on install event (when the service worker is invoked for the first time) will open the cache (line 14), and will add all urls to the cache (line 17) and then return the contents.
– line13: will simply prevent service worker to be killed before finishing with caching the resources.
– line 23: added fetch event. This will fires wherever a web page request a resource (*.js, *.css, images or in this case AJAX request)
– line 26: will return the asset right away from cache
– line29: will fetch the asset from the ‘slow’ service to update the cache for the next request. Now let’s look at the helper methods:
–fromCache function is simply opens the cache and finds the request key.
– update function is opening the cache, fetches a fresh data from the service (or stores a fresh copy of web resource) and puts it back into the cache using request as a key.
Testing the project.
Open an empty tab in Chrome (comand + T), make sure that the dev tools are also open on the net tab (alt + comand + i), and the console drawer is visible (escape toggles the drawer)
You will see the message “My fancy new site !” and after around 5 seconds current date and time will appear (- 5 seconds that took for the service to return the data)
Now service worker is installed. Reload the page and you will see that the current time will appear instantly from the cache. Observe the net tab and you will notice that the service worker also made a request to the service to update the data (the one that is pending on the picture below) and also web resources are now coming from the service worker (the size column).
Additionally if you want to clear the cache you could do it by clicking on the Application tab in the dev tools “Clear Storage” and then “clear site data”.
Here is a simple and a bit silly example of using promises. In real life we could just move the logic into done and fail functions which are promises themselves but for the sake of explaining how promises work we are going to do this example.
var promise = new Promise(function(resolveFunc, rejectFunc) {
$.ajax({
url: "https://www.toni-develops.com/external-files/examples/service-workers/delayed-response.php",
})
.done(function( data ) {
resolveFunc(data);
})
.fail(function(error) {
rejectFunc(error);
});
});
promise.then(
function(result) { // resolve func
console.log('Success! Data: ', result);
}, function(error) { // reject function
console.log("Error! ", error);
})
console.log("Promise was called, and once it's fulfilled the code inside the 'then' function will be executed!");
What’s happening here: – Line 1 creates a new promise, which does AJAX request and calls done or fail functions.
– If execution is successful and done function is called, it also invokes resolveFunc passing the result data.
– Line15 is where the promise has been called, and the bodies of resolveFunc (line 16) and rejectFunc (line 18) are defined and passed to the promise.
Run the code and you should see first the message “Promise was called, and once it’s fulfilled the code inside the ‘then’ function will be executed!”
Five seconds later the newly fetched content will be shown since the service intentionally takes 5 seconds to return the data for testing purposes.
What if we want to make it much easier? Let’s use async and await
async function fetchData() {
var result = await fetch('https://www.toni-develops.com/external-files/examples/service-workers/delayed-response.php');
return result;
}
(async () => {
var r = await fetchData();
console.log("ONE::", r);
console.log("DONE !");
})();
If we ever needed to listen for DOM events, one of the first options that comes to our mind might be to use setTimeout and periodically check the contents of the tag that we are interested in. Like in this example:
But this is intense JavaScript process, which could be done using more elegant way with the help of Mutation Observer.
Using Mutation Observer we could subscribe for different types of DOM events and trigger function on change.
example code:
<html>
<body>
<div id="root">loading ...</div>
<script>
setTimeout(function() {
document.getElementById('root').innerHTML = "My content is loaded!";
}, 3000);
</script>
<script>
var targetNode = document.getElementById('root');
var config = { attributes: true, childList: true, subtree: true, characterData:true };
var observer = new MutationObserver(function() {
alert("#root div changed!");
// stop observing
observer.disconnect();
});
observer.observe(targetNode, config);
</script>
</body>
</html>
what is happening here: – created a div tag with id=’root’ and added ‘Loading …’ text.
– created MutationObserver (line 14) to listen for DOM changes in ‘root’ folder.
– specified which DOM events will trigger the function in config param (line 13)
– after 3 sec. setTimeout event fires and changes the content of the ‘root’ div, triggering DOM change event which will cause the function set in MutationObserver (line 14) to be executed.
– after showing the alert we could optionally disconnect and unsubscribe for DOM change events for this tag.
At a minimum, one of childList, attributes, and/or characterData must be true when you call observe(). Otherwise, a TypeError exception will be thrown.
An array of specific attribute names to be monitored. If this property isn’t included, changes to all attributes cause mutation notifications. No default value.
Set to true to record the previous value of any attribute that changes when monitoring the node or nodes for attribute changes; see Monitoring attribute values in MutationObserver for details on watching for attribute changes and value recording. No default value.
Set to true to record the previous value of a node’s text whenever the text changes on nodes being monitored. For details and an example, see Monitoring text content changes in MutationObserver. No default value.
Set to true to monitor the target node (and, if subtree is true, its descendants) for the addition or removal of new child nodes. The default is false.
Set to true to extend monitoring to the entire subtree of nodes rooted at target. All of the other MutationObserverInit properties are then extended to all of the nodes in the subtree instead of applying solely to the target node. The default value is false.