Could Not Do a Physical-order Read to Fetch Next Row.

At that place's been some confusion around the new fetch API recently. Let's clear things upward.

The first matter you lot'll notice about fetch is it's a massive improvement on XMLHttpRequest in terms of API design. Here's how to become some JSON using XHR:

                          var              xhr              =              new              XMLHttpRequest              (              )              ;              xhr.              open              (              'GET'              ,              url)              ;              xhr.responseType              =              'json'              ;              xhr.              onload              =              function              (              )              {              console.              log              (xhr.response)              ;              }              ;              xhr.              onerror              =              role              (              )              {              console.              log              (              'Booo'              )              ;              }              ;              xhr.              send              (              )              ;                      

At present mop up that vomit and take a look at how fetch does the same thing:

                          fetch              (url)              .              and so              (              function              (              response              )              {              render              response.              json              (              )              ;              }              )              .              then              (              function              (              data              )              {              panel.              log              (data)              ;              }              )              .              grab              (              function              (              )              {              console.              log              (              'Booo'              )              ;              }              )              ;                      

Mix in some ES6 arrow functions and information technology gets even more than compact:

                          fetch              (url)              .              so              (              (              r              )              =>              r.              json              (              )              )              .              then              (              (              data              )              =>              console.              log              (data)              )              .              catch              (              (              e              )              =>              panel.              log              (              'Booo'              )              )              ;                      

And with ES7 async functions it can exist structured like sync lawmaking, whilst even so being async:

                          (              async              (              )              =>              {              try              {              var              response              =              await              fetch              (url)              ;              var              information              =              await              response.              json              (              )              ;              console.              log              (information)              ;              }              take hold of              (e)              {              console.              log              (              'Booo'              )              ;              }              }              )              (              )              ;                      

Unfortunately, not everyone was throwing "SHUT UP AND TAKE MY MONEY" memes at it. One particular high-profile JavaScript community member was unconvinced…

Dramatic reconstruction

They thought we shouldn't be calculation high level features to the platform, especially as we're in dire need of lower level primitives when it comes to requests and responses.

To that I say, well, really, that'south soooo fetch, and we are going to brand fetch happen. Let's articulate upwardly the misconceptions…

Don't be fooled past the nice API

A nice, meaty API can exist a sign of "high level", and therefore restrictive. Simply that isn't the example here. XHR is so bad, a lower-level and more than featureful API tin can exist also simpler and easier to utilize.

XHR is at present defined in terms of fetch (see the calls to fetch in XHR'southward .transport()), meaning fetch is lower level than XHR.

Fetch isn't done even so

What exists in Chrome today doesn't embrace the full spec, and the spec doesn't cover all of the planned features. In some cases this is because they haven't been designed notwithstanding, in others information technology'south because they're dependent on other in-progress specs. Basically, nosotros're post-obit this pattern:

Developing iteratively so the user starts with a skateboard, scooter, bike, motorbike then car, as opposed to giving them nothing until you give them the whole car.

Bit of a bluster: it bothers me that as developers, we preach iterative evolution and release, but when nosotros're the customers of that arroyo the reaction is all too oftentimes "HOW DARE Yous PRESENT ME WITH SUCH INCOMPLETE IMPERFECTION".

The alternative would take been to sit on the feature for months (or years?) instead of getting large parts of it into developers' hands today. Iterative release too means we've been able to become feedback from existent-globe usage, and that steers future iterations in terms of blueprint and priority.

Anyhow, let'south take a expect at what fetch tin do that XHR cannot:

Asking/Response primitives

XHR kinda smushes the request and response together, meaning they can't be used separately. Fetch is different cheers to the Asking and Response constructors. This is especially useful within a ServiceWorker:

            self.              addEventListener              (              'fetch'              ,              role              (              issue              )              {              if              (event.request.url              ===              new              URL              (              '/'              ,              location)              .href)              {              effect.              respondWith              (              new              Response              (              '<h1>Hi!</h1>'              ,              {              headers:              {              'Content-Type'              :              'text/html'              }              ,              }              )              ,              )              ;              }              }              )              ;                      

In the above, outcome.asking is a Asking. There'due south no response yet, and instead of letting the browser go to the network I respond with my ain Response. Alternatively, I could get the response from the network using fetch(event.asking), or fifty-fifty get a response from the enshroud.

The Enshroud API is a shop of Responses keyed against Requests, having separates allows you to add your own pairings.

This is in Chrome stable today from within a ServiceWorker. The fetch API is besides available from pages in Chrome Beta.

Soon, request.context will exist able to tell yous the source of that request, so you tin tell apart requests triggered by hyperlinks vs <img> etc.

'no-cors' and opaque responses

If I request //google.com from this site using XHR or plain fetch it will fail. This is because it'due south a CORS request and the response doesn't have CORS headers.

Yet, with fetch, you tin make a no-cors request:

                          fetch              (              '//google.com'              ,              {              manner:              'no-cors'              ,              }              )              .              and then              (              function              (              response              )              {              console.              log              (response.blazon)              ;              // "opaque"              }              )              ;                      

This is like to the request an <img> makes. Of course, you tin't read the content of the response as information technology could contain private information, but it can be consumed by other APIs:

            cocky.              addEventListener              (              'fetch'              ,              function              (              event              )              {              upshot.              respondWith              (              fetch              (              '//www.google.co.uk/images/srpr/logo11w.png'              ,              {              way:              'no-cors'              ,              }              )              ,              )              ;              }              )              ;                      

The above is fine within a ServiceWorker, as long every bit the receiver is happy with a no-cors response. <img> is, <img crossorigin> isn't.

Yous can also store these responses in the Cache API for use later on, which is great for CDN content such as scripts, CSS, and imagery, which often lack CORS headers.

For more on the origin of CORS, come across Anne VK'south article on same-origin policy.

This all works in Chrome stable today. In Chrome Canary, although y'all can use fetch() from a page, no-cors isn't enabled there yet (ticket).

Streams

XHR lacks streaming. You can get admission to .responseText while the request is in progress, but the whole response is still going to buffer into memory.

With fetch, you lot become access to the low-level body stream. Say I wanted to load a massive CSV and find the value in the cell after the one containing "Jake":

                          fetch              (              '/large-data.csv'              )              .              and then              (              function              (              response              )              {              var              reader              =              response.body.              getReader              (              )              ;              var              partialCell              =              ''              ;              var              returnNextCell              =              false              ;              var              returnCellAfter              =              'Jake'              ;              var              decoder              =              new              TextDecoder              (              )              ;              function              search              (              )              {              return              reader.              read              (              )              .              then              (              function              (              outcome              )              {              partialCell              +=              decoder.              decode              (result.value              ||              new              Uint8Array              (              )              ,              {              stream:              !upshot.done,              }              )              ;              // Split what nosotros have into CSV 'cells'              var              cellBoundry              =                              /                (?:,|\r\n)                /                            ;              var              completeCells              =              partialCell.              separate              (cellBoundry)              ;              if              (              !result.done)              {              // Concluding cell is likely incomplete              // Go along hold of it for next time              partialCell              =              completeCells[completeCells.length              -              one              ]              ;              // Remove it from our complete cells              completeCells              =              completeCells.              slice              (              0              ,              -              1              )              ;              }              for              (              var              cell              of              completeCells)              {              jail cell              =              jail cell.              trim              (              )              ;              if              (returnNextCell)              {              reader.              abolish              (              'No more reading needed.'              )              ;              return              jail cell;              }              if              (cell              ===              returnCellAfter)              {              returnNextCell              =              true              ;              }              }              if              (result.done)              {              throw              Error              (              'Could not find value later '              +              returnCellAfter)              ;              }              return              search              (              )              ;              }              )              ;              }              return              search              (              )              ;              }              )              .              so              (              office              (              consequence              )              {              panel.              log              (              "Got the outcome! It's '"              +              result              +              "'"              )              ;              }              )              .              grab              (              part              (              err              )              {              panel.              log              (err.message)              ;              }              )              ;                      

Here I'm reading through the CSV (yep, I know my regex is naive), but with only a chunk of content in memory at a given time. In one case I find the value I'm looking for, I cancel the stream, closing the connection.

response.torso is a ReadableStream as defined by the streams spec. Streaming was planned from the outset, but it's one of the bits nosotros launched without as the spec was still in progress.

TextDecoder is part of the encoding spec. If the chunk information technology receives via .decode(input, {stream: true}) ends with a partial multi-byte grapheme, it will return and flush everything just that partial. The adjacent phone call to decode appends onto the partial, hopefully forming a whole character.

This stuff is starting to land in Canary, here'due south a demo of the above, and hither'due south a demo with a larger dataset (alert: running the demo may download many megabytes).

Streams are one of the things I'm actually looking forward to having on the platform. I want to exist able to stream-parse some JSON, generate some HTML as a consequence, and stream that to the browser'southward parser. JS-driven apps lack an easy fashion to get progressive-rendering from a single data source, streams can solve that.

Transform streams are coming soon, which would brand the lawmaking higher up simpler. Ideally TextDecoder would be a transform stream, and some other transform stream could chunk it into CSV rows. Something like:

                          fetch              (              '/large-data.csv'              )              .              and so              (              function              (              response              )              {              var              csvStream              =              response.trunk              .              pipeThrough              (              new              TextDecoder              (              )              )              .              pipeThrough              (              new              CSVDecoder              (              )              )              ;              csvStream.              read              (              )              .              then              (              function              (              result              )              {              // array of cell values for the first row              panel.              log              (consequence.value)              ;              }              )              ;              }              )              ;                      

Transform streams also become really exciting within a ServiceWorker:

            self.              addEventListener              (              'fetch'              ,              function              (              event              )              {              event.              respondWith              (              fetch              (              'video.unknowncodec'              )              .              then              (              office              (              response              )              {              var              h264Stream              =              response.body              .              pipeThrough              (codecDecoder)              .              pipeThrough              (h264Encoder)              ;              return              new              Response              (h264Stream,              {              headers:              {              'Content-type'              :              'video/h264'              }              ,              }              )              ;              }              )              ,              )              ;              }              )              ;                      

In the to a higher place, I'thou using transform streams to take a video the browser doesn't empathize, decode it with JS, and encode information technology in a format the browser does understand. It'd be amazing to meet if the browser could do this in real time.

Stream readers & cloning

Every bit I mentioned before, we initially shipped fetch without streams back up and then developers could become the other benefits sooner. To make up for a lack of streams & to later on offer a simple way to get mutual information types, we added some readers:

                          fetch              (url)              .              so              (              part              (              response              )              {              return              response.              json              (              )              ;              }              )              ;                      

That, every bit you might expect, reads the whole stream as JSON. Here's the total list of readers:

  • .arrayBuffer()
  • .blob()
  • .formData()
  • .json()
  • .text()

They exist on Request objects too equally responses, and then you lot can use them to read (for example) Mail data inside a ServiceWorker.

These are true stream readers, meaning they bleed the stream:

                          fetch              (url)              .              then              (              part              (              response              )              {              return              response.              json              (              )              .              grab              (              function              (              )              {              // This does not work:              return              response.              text              (              )              ;              }              )              ;              }              )              ;                      

The call to .text() fails as the stream has already been read. You can piece of work around this using .clone():

                          fetch              (url)              .              then              (              office              (              response              )              {              render              response              .              clone              (              )              .              json              (              )              .              catch              (              function              (              )              {              render              response.              text              (              )              ;              }              )              ;              }              )              ;                      

.clone() opts you into buffering. The clone gets read as JSON, only the original is even so at that place and can exist read in another format. Of course, this means the raw response information needs to exist kept around in retention until all copies are read or garbage nerveless.

Alternatively, yous could wait at the headers of the response:

                          fetch              (url)              .              then              (              function              (              response              )              {              if              (response.headers.              get              (              'Content-Type'              )              ===              'application/json'              )              {              return              response.              json              (              )              ;              }              render              response.              text              (              )              ;              }              )              ;                      

This is another feature fetch has over XHR, you lot can decide which format to read the body as subsequently you've inspected the headers.

Other bits

In that location are more features that fetch has over XHR that I'm not going to encompass in besides much detail, they include:

Fetch has a headers class which tin can be used to read/write headers, and has an ES6 iterator.

Enshroud control

The cache mode lets you specify the interaction with the cache. As in, should the cache be consulted? Should the response get into the cache if it'southward valid? Should the response but come from the cache?

The latter is a scrap contentious as it can expose user history, so it may come with a CORS restriction before information technology lands in Chrome.

No-credential aforementioned-origin requests

XHR forces yous to serve credentials with requests to the same origin, fetch doesn't. In fact, no-credentials is the default for all requests made by fetch, making it less magic than XHR.

What'due south missing?

Of course, there are some features XHR has that fetch doesn't have.

Request aborting

This is the big one. In Canary you can cancel the stream, but there'southward no mode to abort a request before headers have arrived.

We're going to fix this using cancellable promises, which other specs will benefit from. Track the discussion of this over at GitHub.

Progress events

Progress events are a loftier level characteristic that won't arrive in fetch for at present. Y'all tin create your own past looking at the Content-Length header and using a pass-through stream to monitor the bytes received.

This means you can explicitly handle responses without a Content-Length differently. And of course, even if Content-Length is at that place information technology can be a lie. With streams you can handle these lies still you lot want.

Synchronous requests

Noooope. The fetch spec documents them, but they won't be office of the API. Sync requests are awful.

Couldn't this have been congenital on summit of XHR?

XHR was an ugly infant and time has not been kind to it. Information technology's 16 at present. In a few years information technology'll be old enough to drink, and it's enough of a pain in the arse when it'southward sober.

Certain, a lot of this stuff could take been hacked on summit of XHR, simply it would have been a hack. Not only did fetch give us an opportunity to add lower-level features without the cruft of a badly designed API, it immune usa to create a better API for the unproblematic things, using modern JavaScript features similar promises and iterators.

If you want to end using XHR today, in that location's a fetch polyfill. This is built on superlative of XHR, so information technology can't do the stuff that XHR tin can't, but information technology does give you the benefits of a nicer API.

Let'due south make fetch happen!

Farther reading

  • Intro to ServiceWorkers
  • ES6 iterators
  • ES7 async functions
  • Partial fetch polyfill - congenital on top of XHR

Thanks to Matt Gaunt, Mat Scales, Anne van Kesteren, Domenic Denicola, Mikeal Rogers, Ben Kelly, and Joshua Bell for disguising the fact I can't really spell, grammar, or indeed code.

lomaxmach1952.blogspot.com

Source: https://jakearchibald.com/2015/thats-so-fetch/

0 Response to "Could Not Do a Physical-order Read to Fetch Next Row."

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel