您现在的位置是:首页 >技术杂谈 >网络请求实战-实战Fetch和Promise相关的架构网站首页技术杂谈

网络请求实战-实战Fetch和Promise相关的架构

路人i++ 2023-05-29 08:00:02
简介网络请求实战-实战Fetch和Promise相关的架构

目录

Promise神器(承诺)

Promise+Coding示例

Promise常见用法

简单的promise

Fetch的基本用法

fetch

Fetch基本用法

Fetch+Promise场景举例

小结


Promise神器(承诺)

Promise+Coding示例

  • 代表异步求值的过程结果

 promise链式调用,可以一直promise下去

// example 01
const promise = new Promise((resolve, reject) => {
  resolve(100)
}).then(data => {
  console.log(data)
})
// 100
// example 02
const promise = new Promise((resolve, reject) => {
  resolve(100)
}).then(data => {
  console.log(data)
  return 'abc' // 直接返回一个值
})
.then(data => { // 此处不用执行promise了,直接fulfilled了一个'abc
  console.log(data)
})
// 100 abc
// example 03
function wait(ms = 1000, data) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(data)
    }, ms)
  })
}
const promise = new Promise((resolve, reject) => {
  resolve(100)
}).then(data => {
  console.log(data) // 100
  return wait(1000, 'abc') // 等待1s,返'abc'
})
.then(data => {
  console.log(data) // 等待1000ms,打印'abc'
})
// example04
const promise = new Promise((resolve, reject) => {
  reject("some error")
}).then(data => {
  console.log("1", data) // 不执行
}).catch(ex => {
  console.error(ex) // some error
  return "GO"
}).then(data => {
  console.log(data) // GO
})
// example05
function wait(ms = 1000, data){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(data)
    }, ms)
  })
}
async function foo(){
  console.log('--begin--')
  const one = await wait(1000, 1) 
  console.log('--tick 1--') // 等待一秒 
  const two = await wait(1000, 2)
  console.log('--tick 2--') // 再等待一秒 
  console.log(one, two) // 1, 2
  await Promise.reject('some error')
  try{
    await Promise.reject('some error')
  } catch(ex) {
    console.log(ex)  // some error
  }
}
foo()
// example 6
function wait(ms = 1000, data){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(data)
    }, ms)
  })
}
// 工厂方法
Promise.all([wait(200, 1), wait(100, 2)])
  .then(data => {
    console.log('all', data) // 等2个promise都结束,返回这个数组[1,2]
  })
Promise.race([wait(200, 1), wait(100, 2)])
  .then(data => {
    console.log('race', data) // race是在这个执行promise数组中返回第一个拿到的值
  })
  

Promise常见用法

  • resolve & reject
  • Chain (链执行)
  • 并发和竞争(all 和 race)
  • 异常处理 (try catch)

简单的promise

const PENDING = 1
const FULLFILLED = 2
const REJECTED = 3
class Promise{
  constructor(executor){
    this.state = PENDING
    const resolver = (value) => {
      if(this.state === PENDING) {
        this.state = FULLFILLED 
        this.value = value 
      }
      for(let [onFullFill, resolve] of this.fullfills) {
        const x = onFullFill(this.value)
        resolve(x)
      }
    }
    const rejector = () => {
      this.state = REJECTED
    }
    this.fullfills = []
    executor(resolver, rejector)
  }
  then(onFullfill) {
    return new Promise((resolve, rejector) => {
      switch(this.state) {
        case FULLFILLED:
          const x = onFullfill(this.value)
          resolve(x)
          break
        case PENDING:
          this.fullfills.push([onFullfill, resolve])
          break
      }
    })
  }
}
new Promise((resolve) => {
  setTimeout(() => {
    resolve('123')
  })
}).then(data => {
  console.log(data) // 123
  return '456' 
}).then(data => {
  console.log(data) // 456
  return data
})

Fetch的基本用法

fetch

一个让处理http pipeline更容易的工具(MDN)

  • 返回Promise
  • Resolve发生在网络通信正常(404,500也是resolve)
  • Reject发生在网络通信异常
  • 默认不接受cookie(需要设置)

存在队头阻塞,服务端负载充足

Fetch基本用法

  • GET/POST/PUT/DELETE
  • Headers
  • Cookie
  • 缓存
fetch("/product", {
  method: "POST",
  headers : {
    "Content-Type" : "application/json"    
  },
  body: JSON.stringify({ name: "123苹果" }),
}).then((resp) => console.log(resp.status))
fetch("/product", {
  method: "POST",
  headers : {
    "Content-Type" : "application/json"    
  },
  body: JSON.stringify({ name: "".padStart(100000, "A")}),
}).then((resp) => console.log(resp.status))
// node端 需要引用实例
const fetch = require('node-fetch')
const promise = fetch('https://www.baidu.com', {
    method: 'POST',
    headers: {
        'Content-Type': "application/json",    
    },
    credentials: 'include', // 设置之后可以接收服务端的Cookie
})
async function foo() {
   const resp = await fetch("http://www.baidu.com", {
       headers: {
           'user-agent': "Mozillia"       
       }   
   })
   const text = await resp.text()
   console.log(text)
}
foo()
// 缓存
fetch("https://www.baidu.com", {cache: 'force-cache'})

Fetch+Promise场景举例

1.指数补偿,专门应付移动端频繁网络波动的

按照指数的时间倍数重复发送请求

  • 0ms
  • 200ms
  • 400ms
  • 800ms
  • 1600ms
  • 3200ms
  • fail
// 示例程序
const fetch = require('node-fetch')
function request(url){
  let resolved = false
  let t = 1 
  return new Promise((resolve) => {
    function doFetch(){
      if(resolved || t > 16) {
        return
      }
      
      fetch(url).then((resp) => {
        return resp.text()
      })
      .then(data => {
        if(!resolved) {
          resolved = true
          resolve(data)
          console.log('t=', t)
        }
      })
      .catch(ex => {
        console.error(ex)
      })
      setTimeout(() => {
        doFetch()
        t *= 2
      }, t * 100)
    }
    doFetch()
  })
}
request('http://www.baidu.com')
  .then(data => {
    console.log(data.length)
  })
setTimeout(() => {
}, 3000)
// 有缺陷,所有请求都发送了
function wait(ms, f) {
    return new Promise((resolve) => {
        setTimeout(()=> {
            resolve(f())        
        }, ms)    
    })
}
function request(url) {
    Promise.race([
        fetch(url),
        wait(100, ()=> fetch(url) ),
        wait(200, ()=> fetch(url) ),
        wait(400, ()=> fetch(url) ),
        wait(800, ()=> fetch(url) ),
        wait(1600, ()=> fetch(url) ),
    ])
}
request('http://www.baidu.com')
  .then(resp => {
    console.log(resp)
  })
setTimeout(() => {
}, 3000)

2.并发处理和时间窗口,底层前端请求优化的

多个资源并发请求(Promise.all)

基于时间窗口过滤重复请求

const fetch = require('node-fetch')
function hash(args) {
  return args.join(',')
}
function window_it(f, time = 50) {
  let w = {} 
  let flag = false 
  return (...args) => {// 参数传递
    return new Promise((resolve) => {
      if (!w[hash(args)]) {
        w[hash(args)] = {
          func: f,
          args,
          resolvers: [],
        }
      }
      if (!flag) {
        flag = true
        setTimeout(() => {
          Object.keys(w).forEach((key) => {
            const { func, args, resolvers } = w[key]
            const promise = func(...args)
              .then((resp) => resp.text())
              .then((text) =>
                resolvers.map((r) => {
                  r(text)
                })
              )
          })
          flag = false
        }, time)
      }
      console.log(args)
      w[hash(args)].resolvers.push(resolve)
    })
  }
}
const request = window_it(fetch, 20)
request('http://www.baidu.com')
  .then(txt => console.log(txt.length))
request('http://www.baidu.com')
  .then(txt => console.log(txt.length))
request('http://www.baidu.com')
  .then(txt => console.log(txt.length))
request('http://www.zhihu.com')
  .then(txt => console.log(txt.length))
request('http://www.baidu.com')
  .then(txt => console.log(txt.length))
request('http://www.baidu.com')
  .then(txt => console.log(txt.length))

小结

  • 为什么不教Axios? 因为fetch是浏览器提供的标准
  • 优化问题要工程化处理(所有页面的优化,请求优化,页面大小优化,白屏的优化【需要用到算法,数据结构和相关的架构知识,是针对一类问题提供工具】)
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。