The most common question I hear post intro to redux is: "How do I fetch some data in actions?"
Most of them would hit the roadblock with: Actions must be plain objects. Use custom middleware for async actions. that is because Actions are meant to be plain JavaScript objects and must have a type property that indicates the type of action being performed.
Let us see a quick example to make an API request say this xkcd comic API.
As there is no community consensus for handling async actions and there are manylibsout there that will make things easier in handling async actions, but in this example below we shall take the vanilla approach.
Let us start with an initial state that looks like:
So, explaining async actions in Redux to a friend, what do you suggest?
0. Talk about Thunk/Promise middlewares for Redux? 1. Do something more vanilla like below? 2. Cases where a middleware would be mandatory? pic.twitter.com/hOW6KG6l7B
Lately /me came across a app-crash whose logs were indicating that the issue was in the logger itself, the error read: "URIError: malformed URI sequence"
Digging through the debug logs I reached a point in the logger were there was log for an a Object which looked like:
1
querystring.unescape(querystring.stringify(obj))
Where querystirng was node's inbuilt querystring lib.
Test case was all green for this particular method, I tired to break this with different types of inputs and wasn't able to break it at one shot.
Further, digging into the node source, querystring there was this sweet line throw new URIError('URI malformed'); which was exactly i was looking for and the comment also read: // Surrogate pair, that was a big clue, so played around a bit with unicode surrogate pair and after a while I was able to reproduce the error!
So a half surrogate pair, anything in the range of \uD800 to \uDFFF would break querystring.stringify:
varpromise=newPromise(function(resolve,reject){// Some async...if(/* Allz well*/){resolve("It worked!");}else{reject(Error("It did not work :'("));}});
12345
promise.then(function(result){console.log(result);// "It worked!"},function(err){console.log(err);// Error: "It don not work :'("});
async.map(['file1','file2','file3'],fs.stat,function(err,results){// results is now an array of stats for each file});async.filter(['file1','file2','file3'],fs.exists,function(results){// results now equals an array of the existing files});async.parallel([function(){...},function(){...}],callback);async.series([function(){...},function(){...}]);
A: Happ(Y)ily ever after?
M: No! We have some more issues:
Streams are broken, callbacks are not great to work with, errors are vague, tooling is not great, community convention is sort of there..
letCountIter=Counter();CountIter.next();// Would result in { value: 0, done: false }// Again CountIter.next();//Would result in { value: 1, done: false }
functiontheAnswer(){varstate=0;return{next:function(){switch(state){case0:state=1;return{value:42,// The yielded value.done:false};case1:return{value:undefined,done:true};}}};}
M: But, this has not yet solved the initial issue!
varurls=[/* Huge list */];// sequentialco(function*(){for(vari=0;i<urls.length;i++){varurl=urls[i];varres=yieldget(url);console.log('%s -> %s',url,res[0].statusCode);}})()// parallelco(function*(){varreqs=urls.map(function(url){returnget(url);});varcodes=(yieldreqs).map(function(r){returnr.statusCode});console.log(codes);})()
M: Interesting? What more?
1
$ npm install co-sleep
123456789
varsleep=require('co-sleep');varco=require('co');co(function*(){varnow=Date.now();// wait for 1000 msyieldsleep(1000);expect(Date.now()-now).to.not.be.below(1000);})();
<scripttype="application/javascript"src="task.js"></script><!--'yield'and'let'keywordsrequireversionopt-in--><scripttype="application/javascript;version=1.8">functionhello(){let{spawn,sleep}=task;spawn(function(){// Firefox does not yet use the function* syntaxalert("Hello...");yieldsleep(1000);alert("...world!");});}</script>
Web Applications are alive only if the network is reachable, if you are not connected to the network you ending you seeing an error page, this has been the major drawback of the web content delivery when compared of other technology stacks.
The service worker comes to the rescue at this scenario which provides a Web Worker context, which can be started by a runtime when navigations are about to occur and can be consulted when navigations occur to that location, network requests are dispatched to this worker and it can over-ride the default network stack behavior.
Conceptually, the worker is between the network and a document renderer, allowing the it to provide content for documents, even while offline!
With previous effort to provide offline support, using HTML5 Application Cache, we have also experienced that several attributes of the design contribute to unrecoverable errors.. As a result, the major design principle of the service worker is ‘error recoverability ’. Service workers are started and kept alive by their relationship to events, not documents.
This behaviour is highly influenced by Shared Workers and Event Pages in the chromium extensions model.
Service workers gives us an overall benefit in having an excellent user experience as it’s gives support out of the box for offline support, instead of seeing an ‘You are offline message’, one could give a smoother offline-first experience. As a result of caching one can provide faster experience with lesser bandwidth consumption, combined with the power of
‘Add to homescreen’, ‘Push Notifications’ and more it would be easy to create truly progressive applications on par, and often better experience than native apps!
Service workers may be started by user agents without an attached document and may be killed by the user agent at nearly any time.
Service worker definition from the spec would be, “Service workers are generic, event-driven, time-limited script contexts that run at an origin.“
In simple terms we can consider it as a Shared Workers that can start, process events, and die without ever handling messages from documents and may be started and killed many times a second.
These special power of a service worker makes them a good candidate for a range of runtime services that may outlive the context of a particular document, such as handling push notifications, background data synchronization, responding to resource requests from other origins, or receiving centralized updates to expensive-to-calculate data.
Mind of a Service Worker.
Here are some attributes of what a service worker thinks and acts like:
It executes in the registering service worker client's origin.
Has a state, which is one of parsed, installing, installed, activating, activated, and redundant.
It has a script URL.
It has an associated containing service worker registration, which contains itself.
Has an associated id (a UUID), which uniquely identifies itself during the lifetime of its containing service worker registration.
Lifecycle events being install and activate.
Functional events including fetch.
Has a script resource map which is a List of the Record {[[key]], [[value]]} where [[key]] is a URL and [[value]] is a script resource.
Has a skip waiting flag. Unless stated otherwise it is unset.
Has an imported scripts updated flag. It is also initially unset.
Before we get up and running with service workers, there are few important things to keep in mind.
A service worker runs in a worker context, has no DOM access, is non-blocking, fully async and hence APIs such as synchronous XHR and localStorage can't be used inside a service worker. Service workers only run over HTTPS, for security reasons.
Service workers has no access to DOM, but can access:
The navigator object
The location object (read-only)
setTimeout()/clearTimeout() and setInterval()/clearInterval()
The Application Cache
Importing external scripts using the importScripts() method
Other service workers.
Service workers need to be on HTTPS only for certain reasons like:
Better to protect end users from man-in-the-middle attacks
Do good by encouraging HTTPS adoption
Existing "playground" services (e.g. github.io) now work with HTTPS
HTTPS is coming across much more of the web quickly
Devtools can loosen the restriction for development (file://, localhost, etc.)
Here is how one would register for a ServiceWorker:
In this example, /pwa/sw.js is the location of the ServiceWorker script, and it controls pages whose URL begins with /pwa/. Note that the scope is optional, and defaults to /.
.register returns a promise which will resolve to based on whether the serviceworker was registered or not. P.S: pwa is your progress web app ;)
It’s important to note that, the page where the service worker is registered, must have been served securely i.e on HTTPS without any certificate errors and script must be on the same origin as the page unless you are using importScripts.
Life cycle:
On .register the worker script goes through three stages:
Download
Install
Activate
You can use the events to interact with install and activate phases:
event.waitUntil basically extends the installation process, once done the activate event is triggered.
123
self.addEventListener('activate',function(event){// Have fun!});
So finally we are all set the control the page?
Well, not yet.
The document we called .register from isn't being controlled yet as the ServiceWorker wasn’t there for the first load, a document will pick a ServiceWorker to be its controller when it navigates to the same. If you refresh the document, it'll be under the ServiceWorker's control and it will set at navigator.serviceWorker.controller.
Friends with the Network
There is this event called fetch that will be fired when:
Navigations within the Service Worker's scope.
All most all the Requests triggered by the page which have registered to that Service Worker.
Given that the service worker has only readonly access to the navigator.location it is not possible to change the location from within the service worker, but with WindowClient.navigate API it is easy to do a navigation!
The navigate() method of the WindowClient interface loads a specified URL into a controlled client page then returns a Promise that resolves to the existing WindowClient.
Say, you want to navigate to a particular URL (/meow.html in this case ;)) after the service worker is activated, you could something like:
12345678910111213141516
self.addEventListener('activate',event=>{event.waitUntil(self.clients.claim().then(()=>{/* returns a Promise for a list of service worker clients. type could be: window, worker, sharedworker or all. */returnself.clients.matchAll({type:'window'});}).then(clients=>{returnclients.map(client=>{// `WindowClient.navigate()` is not yet supported in all the browser.if('navigate'inclient){returnclient.navigate('meow.html');}});}));});
Hope with this it's clear now on how a service worker can navigate the clients it controls to a given URL.
Happy navigating! ;)
Update: Directly from the spec:
The navigate() method must run these steps or their equivalent:
Let url be the result of parsing url with entry settings object's API base URL.
If url is failure, return a promise rejected with a TypeError.
If url is about:blank, return a promise rejected with a TypeError.
If the context object's associated service worker client's active worker is not the incumbent settings object's global object's service worker, return a promise rejected with a TypeError.
Let promise be a new promise.
Run these steps in parallel:
Let browsingContext be the context object's associated service worker client's global object's browsing context.
If browsingContext has discarded its Document, reject promise with a TypeError and abort these steps.
Queue a task to run the following substeps on the context object's associated service worker client's responsible event loop using the user interaction task source:
Navigate browsingContext to url with replacement enabled and exceptions enabled. The source browsing context must be browsingContext.
If the navigation throws an exception, reject promise with that exception and abort these steps.
If the origin is not the same as the service worker's origin, then:
Resolve promise with null.
Abort these steps.
Let client be the result of running Capture Window Client algorithm, or its equivalent, with browsingContext as the argument.
If you reading this post, I assume that you are enough awareness of webpack, so I shall be directly diving into the code part of it.
Scenario:
Assume that there are few less file that you need to bundle along with webpack's output but those are not required or imported in any of your source/target files, below is simple trick in webpack config to bundle such files:
/public/styles/external/*.less has those files that are not required in any of src files but needs to bundled, the trick bascially is to create js file for each those less files and then extract them to their respective css files to ./build/css/ path.
So today morning, when I was lurking on #javascript channel on freenode, there was this question:
merpnderp>
If I have var foo = {x:{y:{z:bar}}}; And I have var keys = ['x','y','z']; How would I make something like foo[[keys.join('.')]] = 'baz'; work?
Me and @doodadjs were trying to solve this using [].reduce and then there was this silly thought of extending the same code to proxies
varstr='The quick brown fox had a great Xmas';varreplace=str.replace(/Xmas/i,'Christmas!');
So it's mostly about seach and replace feature that most (P.S: By most of them I mean those I have come across on IRC.) of them wouldn talk about but would have not noticed that the signature of the replace method is like:
1
replace(regexp|substr,newSubStr|function[,flags])
From the signature, the focus of this post will be on the function in the params, this will be invoked to create the new substring and receives the below parameters:
match: The matched substring.
p1,p2..pn: The nth parenthesized submatch string.
offset: The offset of the matched substring.
string: The entire string which is being processed.
Let us see few example application of the same:
A simple example of increasing your protein and fat intake ;)
123
>functionreplacer(match){return2*match}>'10 gms of protein and 5gms of fat.'.replace(/[0-9]+/g,replacer)>'20gms of protein and 10gms of fat.'
The below snippet replaces a Fahrenheit degree with its equivalent Celsius degree, for an input 212F, the function returns 100C, nothing big about this, but if you notice that the replace function's second agrument is a function convert which receives the parameters specified in the table above and returns a string.
12345678910
// from mdnfunctionf2c(temprature){functionconvert(match,p1,offset,string){return((p1-32)*5/9)+'C';}vars=String(temprature);vartest=/(-?\d+(?:\.\d*)?)F\b/g;returns.replace(test,convert);}
This feature would be more useful when you are doing a replace operation over a loop, using a replacer function like this one could totally avoid a loop.
Say, the input is an string that looks like:
1
letstr='___.___.___.__._...__';
Where __. is a high singal and __. is a low singal and rest of them are noise, you can filter them easily with an explicity loop using the replacer function like:
Well, it has been a decent amount of time since I wrote amount some fundamentals of JavaScript, hope this was useful for you, feel free to share few of your experince with replacer functions.