zoukankan      html  css  js  c++  java
  • fetch 的控制器和观察者

    因为受 cancelable promise 的拖延,fetch 一直没有传统的 XHR 所拥有的 abort() 和 onprogress 功能,去年年底 cancelable promise 草案被彻底废弃了,所以今年年初的这几个月里,一份完全新的、fetch 专用的、不依赖 JS 规范、与 Promise 没有任何直接关系的草案诞生了,而且 Firefox 已经率先实现了其中的大部分功能。

    这份草案中引入了 3 种新的对象类型,FetchController(fetch 控制器)、FetchSignal(fetch 信号)、FetchObserver(fetch 观察者),虽然这三个都是全局构造函数,但其实只有 FetchController 对象是真的需要你手动 new 出来的,其它两种对象都是浏览器为你生成的,我们下面分别演示下如何中断(取消、中止)一个 fetch 请求,以及如何监听 fetch 请求的执行进度。

    中断 fetch 请求

    const controller = new FetchController()
    const signal = controller.signal
    
    fetch("https://tc39.github.io/ecma262/", { signal }).then(() => {
      alert("请求成功")
    }).catch(err => {
      if (err.name === "AbortError") {
        alert("请求被中断") 
      }
    })
    
    setTimeout(() => controller.abort(), 500)

    这段代码中,我们先创建了一个 fetch 控制器 controller,每个控制器都自带一个 fetch 信号对象(signal 属性),然后在你用 fetch() 发送请求的时候把这个信号对象带上(signal 参数)。之后,你就可以通过控制器的 abort() 方法,来中断那个 fetch 请求了。只要在你执行 abort() 方法的时候,那个 fetch 请求还没有执行完毕(请求还没发出去、请求发出去了还没接收到响应、响应还没接收完),那么当初 fetch() 返回的那个 promise 就会被 reject,所产生的 error 对象的 name 为 AbortError。

    上面这个例子中,我是在 500 毫秒的时候中断了这个请求,这是个绝对的数字,所以根据你的网速不同,执行代码时你有可能会看到弹出“请求成功”,也有能看到“请求被中断”。

    一个信号对象还可以同时传递给多个 fetch 请求:

    const controller = new FetchController()
    const signal = controller.signal
    
    fetch("https://tc39.github.io/ecma262/", { signal })
    fetch("https://fetch.spec.whatwg.org/", { signal })
    fetch("https://dom.spec.whatwg.org/", { signal })
    
    controller.abort() // 同时中断三个请求

    信号对象有一个 aborted 属性表明自己是否已经被中断过了,一旦被中断,它不可能回到当初的状态,也就是说,你不能二次利用一个已经被中断过的信号对象,把这样的信号对象传给 fetch() 的话,fetch() 会立即结束,不会发出请求:

    const controller = new FetchController()
    const signal = controller.signal
    
    controller.abort()
    
    alert(signal.aborted) // true
    
    fetch("https://tc39.github.io/ecma262/", { signal }) // 直接被 reject,请求不会发出

    信号对象上还可以监听 abort 事件,不过我想一般用不上,因为请求中断后的处理代码一般写在 fetch(...).catch(...) 里面:

    const controller = new FetchController()
    const signal = controller.signal
    
    signal.addEventListener("abort", function(e) { // 使用 signal.onabort = 注册监听函数也可以
      alert(e.type) // abort
      alert(signal.aborted) // true
    })
    
    alert(signal.aborted) // false
    
    controller.abort()

    一个控制器除了可以让自己的信号对象 abort,还可以关注(跟随、监听)一个别的控制器的信号对象,只要关注的这个信号对象被 abort 了,自己的 abort() 方法就会被自动执行,从而自己的信号对象也会被 abort:

    const fc1 = new FetchController()
    const fc2 = new FetchController()
    
    fc1.follow(fc2.signal)
    
    fc2.abort()
    
    alert(fc1.signal.aborted) // true,即便你没有手动执行 fc1.abort()

    再来看个更复杂的例子:

    const fc1 = new FetchController()
    const fc2 = new FetchController()
    const fc3 = new FetchController()
    
    fc1.follow(fc2.signal)
    fc2.follow(fc3.signal)
    
    fetch("https://tc39.github.io/ecma262/", { signal: fc1.signal })
    fetch("https://fetch.spec.whatwg.org/", { signal: fc2.signal })
    fetch("https://dom.spec.whatwg.org/", { signal: fc3.signal })
    
    fc3.abort() //  三个请求都中断了

    一个控制器只能关注一个别人家的信号对象,而一个信号对象可以被任意多个别家的控制器关注。不能直接或间接的关注自己的信号对象,比如上面的代码中再增加一行 fc3.follow(fc1.signal) 就会产生环形关注,所以那句代码会没有任何效果。还有一个 unfollow() 方法可以取消关注。

    总结下就是,一个控制器对象有一个 signal 属性指向一个信号对象,还有 abort()、follow()、unfollow() 这三个方法。一个信号对象有一个 aborted 属性,还有一个 onabort 事件监听函数。

    监听 fetch 请求

    上面说到了 FetchController 和 FetchSignal,接下来要说说 fetch 观察者 - FetchObserver 对象。fetch 观察者对象上可以监听 statechange、requestprogress、responseprogress 三种事件,分别用来获取 fetch 请求的状态信息变化、请求数据发送的大小变化(POST 请求)、响应数据接收的大小变化信息,先来看看如何监听状态信息的变化:

    fetch("https://tc39.github.io/ecma262/", {
      observe(observer) {
        observer.onstatechange = () => { // 也可以用 addEventListener("statechange"...
          alert(observer.state) // "requesting"、"responding"、"aborted", "errored"、"complete" 中的某个值
        }
      }
    })

    FetchObserver 对象不用你手动 new,浏览器会为你 new 一个,在 fetch 请求开始之前,通过你事先指定的 observe 回调函数的参数传递给你。然后你在这个观察者对象上注册 statechange 监听函数,就可以在请求的不同阶段执行特定的代码了。

    监听响应的接收进度使用 responseprogress 事件:

    fetch("https://img.alicdn.com/tfscom/TB1y6icQXXXXXaQXFXXXXXXXXXX.mp4", {
      observe(observer) {
        observer.onresponseprogress = e => { 
          if(e.lengthComputable) {
            console.log("视频下载进度:" + parseInt(e.loaded / e.total * 100) + "%")
          }
        }
      }
    })

    监听 POST 数据的发送进度使用 requestprogress 事件:

    fetch("/upload", {
      method: "POST",
    credentials: "include", body: canvas.toBlob(), observe(observer) { observer.onrequestprogress
    = e => { console.log("图片上传进度:" + parseInt(e.loaded / e.total * 100) + "%") } } })

    在本文发布的现在,规范草案还只是最初的阶段,可能有很多细节没考虑,目前 Firefox 也没有实现我上面所说的所有功能,实现了的也有一些 bug,而且还需要事先在 about:config 手动添加某两个选项才可以使用,所以本文仅仅是简要介绍,大家暂时眼睛看看就够了。

    以后 fetch 控制器的功能可能还会增加,不仅仅能让一个 fetch 请求中断,比如还可以指定这个请求的优先级,fetch 观察者同时也可以用来监听 fetch 请求优先级的变化。

  • 相关阅读:
    tile38 复制配置
    The Guardian’s Migration from MongoDB to PostgreSQL on Amazon RDS
    tile38 一款开源的geo 数据库
    sqler sql 转rest api 的docker 镜像构建(续)使用源码编译
    sqler sql 转rest api javascript 试用
    sqler sql 转rest api redis 接口使用
    sqler sql 转rest api 的docker image
    sqler sql 转rest api 的工具试用
    apache geode 试用
    benthos v1 的一些新功能
  • 原文地址:https://www.cnblogs.com/ziyunfei/p/6617900.html
Copyright © 2011-2022 走看看