2023年前端面试高频考点之 通信(渲染、http、缓存、异步、跨域)

news2024/11/26 17:24:14

目录

浏览器从输入url到渲染页面 过程⭐⭐⭐

Http和Https区别⭐⭐⭐

GET和POST发送请求⭐⭐⭐

异同

 http版本⭐⭐⭐

http状态码⭐⭐⭐

TCP⭐⭐⭐

三次握手

四次挥手

流量控制(滑动窗口机制)

拥塞控制

keep-alive持久连接

TCP⭐⭐⭐

三次握手

四次挥手

流量控制(滑动窗口机制)

拥塞控制

keep-alive持久连接

异步⭐⭐⭐

 Promise(ES6 解决地狱回调)⭐⭐⭐

promise调用then,

兼容同步任务

Promise.prototype.catch()

Promise.prototype.finally()

Promise.resolve()

Promise.reject()

Promise.all()

Promise.race()

Promise.all()哪怕一个请求失败了也能得到其余正确的请求结果的解决方案⭐⭐

Mypromise

fetch(ES6 拉取网络资源)

 async/await函数

跨域通信⭐⭐⭐


浏览器从输入url到渲染页面 过程⭐⭐⭐

查找缓存

  • 合成 URL

浏览区会判断用户输入是合法 URL(Uniform Resource Locator,统一资源定位器),比如用户输入的是搜索的关键词,默认的搜索引擎会合成新的,

如果符合url规则会根据url协议,在这段内容加上协议合成合法的url 

  • 查找缓存

网络进程获取到 URL,

先去本地缓存中查找是否有缓存资源,如果有则拦截请求,直接将缓存资源返回给浏览器进程;

否则,进入网络请请求阶段;    

  • DNS 解析:(域名系统Domain Name System)

DNS 查找数据缓存服务中是否缓存过当前域名信息,有则直接返回;

否则,会进行 DNS 解析返回域名对应的 IP 和端口号

如果没有指定端口号,http 默认 80 端口https 默认 443

如果是 https 请求,还需要建立 TLS 连接;(传输层安全性协议Transport Layer Security)

TCP连接

  • 建立 TCP 连接:

TCP 三次握手与服务器建立连接,然后进行数据的传输;

  • 发送 HTTP 请求

浏览器首先会向服务器发送请求行,它包含了请求方法、请求 URI (统一资源标识符Uniform Resource Identifier HTTP 协议的版本

还会发送请求头,告诉服务器一些浏览器的相关信息,比如浏览器内核,请求域名

  • 服务器处理请求

服务器首先返回响应头+响应行响应行包括协议版本和状态码

  • 页面渲染:

查看响应头的信息,做不同的处理,比如重定向,存储cookie 看看content-type的值,根据不同的资源类型来用不同的解析方式

渲染详情可见2023年最全前端面试题考点HTML5+CSS3+JS_参宿7的博客-CSDN博客

  • 断开 TCP 连接:

数据传输完成,正常情况下 TCP 将四次挥手断开连接

Http和Https区别⭐⭐⭐

1.`HTTP` 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头
2.``HTTP` 无法加密,而HTTPS 对传输的数据进行加密,安全
3.`HTTP` 标准端口是80 ,而 HTTPS 的标准端口是443
4.`在OSI` 网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层

HTTPS是密文传输数据,HTTP是明文传输数据。

HTTPS协议 = HTTP协议 + SSl/TLS协议

用SSL/TLS对数据进行加密和解密

SSL的全称是Secure Sockets Layer,即安全套接层协议

TLS的全称是Transport Layer Security,即安全传输层协议

对数据进行对称加密,对 对称加密所要使用的秘钥 进行非对称加密传输。

  • 服务端的公钥和私钥,用来进行非对称加密
  • 客户端生成的随机秘钥,用来进行对称加密

GET和POST发送请求⭐⭐⭐

HTTP协议中的两种发送请求的方法。

异同

:GET和POST本质上就是TCP链接

数据包数量:GET产生一个TCP数据包;POST产生两个TCP数据包

(并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。)

过程

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

对于POST请求方式,可以将请求数据打包在请求体中,

并通过Headers头部信息里的“Content-Type”字段指定请求体的数据类型为JSON,并

且在服务端返回相应头信息的时候也指定返回类型为JSON。

以下是一个使用POST方法请求并返回JSON类型数据的示例代码:

fetch('yourUrl', { 
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(requestData)
})
.then(function(response) {
  return response.json(); // 解析并返回JSON格式数据
})
.then(function(json) {
  console.log('请求成功返回结果', json);    
})
.catch(function (error) {
  console.log("请求出现了问题::", error);
});

其中 requestData 是一个 JavaScript 对象,可以通过 JSON.stringify() 方法将其转换为 JSON 字符串,然后放到 POST 请求的 Body 中。

应用

在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。

在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。

因为GET一般用于查询信息,POST一般用于提交某种信息进行某些修改操作(私密性的信息如注册、登陆)

所以GET在浏览器回退不会再次请求,POST会再次提交请求

因为GET在浏览器回退不会再次请求,POST会再次提交请求

所以GET请求会被浏览器主动缓存,POST不会,要手动设置
       GET请求参数会被完整保留在浏览器历史记录里,POST中的参数不会

因为 GET请求参数会被完整保留在浏览器历史记录里
所以GET请求在URL中传送的参数是有长度限制的,而POST没有限制

因为GET参数通过URL传递,POST放在Request body
所以GET参数暴露在地址栏不安全,POST放在报文内部更安全

 http版本⭐⭐⭐

1.0->1.1(一次传输多个文件,默认Connection: keep-alive

http1.x解析基于文本

http2.0采用二进制格式,新增特性 多路复用、header压缩、服务端推送(静态html资源)

http状态码⭐⭐⭐

状态码是由3位数组成,第一个数字定义了响应的类别,且有五种可能取值:
    1xx Informational(信息状态码)      接受请求正在处理
    2xx Success(成功状态码)            请求正常处理完毕

    3xx Redirection(重定向状态码)      需要附加操作已完成请求

    4xx Client Error(客户端错误状态码)  服务器无法处理请求
    5xx Server Error(服务器错误状态码)  服务器处理请求出错

常见状态码:

    200 响应成功
    204 返回无内容

    301永久重定向  (请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。)
    302临时重定向(服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。)
    304资源缓存(自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。)

    400 错误请求(请求格式错误,服务器不理解请求的语法。)
    422 无法处理(请求格式正确,但是由于含有语义错误,无法响应)
    401 未授权(请求要求身份验证。)
    403服务器禁止访问
    404服务器找不到请求的网页
    

    500 502服务器内部错误
    504 服务器繁忙

TCP⭐⭐⭐

三次握手

四次挥手

TIME-WAIT:2 MSL (Maximum segment lifetime) 最长报文最大生存时间

流量控制(滑动窗口机制

让发送方的发送速率不要太快,要让接收方来得及接收。

还可接收的窗口是 rwnd = 400 ”(receiver window) 。

发送方的发送窗口不能超过接收方给出的接收窗口的数值。TCP的窗口单位是字节,不是报文段。

拥塞控制

拥塞:资源供不应求

  • 慢开始、拥塞避免
  • 快重传、快恢复

keep-alive持久连接

为了能够提升效率,在 HTTP 1.1 规范中把 Connection 头写入了标准,并且默认启用。

浏览器 或 服务器在HTTP头部加上 Connection: keep-alive,TCP 就会一直保持连接。

它会在隔开一段时间之后发送几次没有数据内容的网络请求来判断当前连接是不是应该继续保留。

可能会造成大量的无用途连接,白白占用系统资源

TCP⭐⭐⭐

三次握手

四次挥手

TIME-WAIT:2 MSL (Maximum segment lifetime) 最长报文最大生存时间

流量控制(滑动窗口机制

让发送方的发送速率不要太快,要让接收方来得及接收。

还可接收的窗口是 rwnd = 400 ”(receiver window) 。

发送方的发送窗口不能超过接收方给出的接收窗口的数值。TCP的窗口单位是字节,不是报文段。

拥塞控制

拥塞:资源供不应求

  • 慢开始、拥塞避免
  • 快重传、快恢复

keep-alive持久连接

为了能够提升效率,在 HTTP 1.1 规范中把 Connection 头写入了标准,并且默认启用。

浏览器 或 服务器在HTTP头部加上 Connection: keep-alive,TCP 就会一直保持连接。

它会在隔开一段时间之后发送几次没有数据内容的网络请求来判断当前连接是不是应该继续保留。

可能会造成大量的无用途连接,白白占用系统资源

异步⭐⭐⭐

  • 异步是通过一次次的循环事件队列来实现的.例如 setTimeout setInterval xmlHttprequest 等
  • 同步阻塞式的IO,在高并发环境会是一个很大的性能问题,

所以同步一般只在基础框架的启动时使用,用来加载配置文件,初始化程序什么的.

node单线程的,网络请求,浏览器事件等操作都需要使用异步的方法。

 Promise(ES6 解决地狱回调)⭐⭐⭐

回调函数
当一个函数作为参数传入另一个函数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行,这种函数就称为回调函数,例如setTimeout

回调地狱:回调函数套回调函数,回调的多重嵌套,会导致代码可读低、编写费劲、容易出错,故而被称为 callback hell

Promise:ES6异步操作的一种解决方案

Promise 构造函数接受一个函数作为参数,该函数是同步的并且会被立即执行,称为起始函数

起始函数包含两个函数参数 resolvereject,分别表示 Promise 成功和失败的状态。

起始函数执行成功时,它应该调用 resolve 函数并传递成功的结果。

起始函数执行失败时,它应该调用 reject 函数并传递失败的原因。

promise 有三个状态:

pending[待定]初始状态,fulfilled[实现]操作成功,rejected[被否决]操作失败

如果不使用 resove 和 reject 两个函数 状态为pendding

Promise 构造函数返回一个 Promise 对象,该对象具有以下几个方法:

  • then:用于处理 Promise 成功状态的回调函数,参数为resolve传递的参数。
  • catch:用于处理 Promise 失败状态的回调函数,参数为reject传递的参数。
  • finally:无论 Promise 是成功还是失败,都会执行的回调函数。
const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    if (Math.random() < 0.5) {
      resolve('success');
    } else {
      reject('error');
    }
  }, 1000);
});
 
promise.then(result => {
  console.log(result);
}).catch(error => {
  console.log(error);
});
new Promise(function (resolve, reject) {
    var a = 0;
    var b = 1;
    if (b == 0) reject("Divide zero");
    else resolve(a / b);
}).then(function (value) {
    console.log("a / b = " + value);
}).catch(function (err) {
    console.log(err);
}).finally(function () {
    console.log("End");
});

promise调用then,

如果非promise,会将值包装成promise

new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log("First");
        resolve();
    }, 1000);
}).then(function () {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log("Second");
            resolve();
        }, 4000);
    });
}).then(function () {
    setTimeout(function () {
        console.log("Third");
    }, 3000);
});
new Promise(function (resolve, reject) {
    console.log(1111);
    resolve(2222);
}).then(function (value) {
    console.log(value);
    return 3333;
}).then(function (value) {
    console.log(value);
    throw "An error";
}).catch(function (err) {
    console.log(err);
});
// then方法
then(resolveFn, rejectFn) {
  //return一个新的promise
  return new MyPromise((resolve, reject) => {
    //把resolveFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
    const fulfilledFn = value => {
      try {
        //执行第一个(当前的)Promise的成功回调,并获取返回值
        let x = resolveFn(value)
        //分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
        //这里resolve之后,就能被下一个.then()的回调获取到返回值,从而实现链式调用
        x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
      } catch (error) {
        reject(error)
      }
    }
    //把后续then收集的依赖都push进当前Promise的成功回调队列中(_resolveQueue), 这是为了保证顺序调用
    this._resolveQueue.push(fulfilledFn)

    //reject同理
    const rejectedFn  = error => {
      try {
        let x = rejectFn(error)
        x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
      } catch (error) {
        reject(error)
      }
    }
    this._rejectQueue.push(rejectedFn)
  })
}
  • 值穿透:根据规范,如果 then() 接收的参数不是function,那么我们应该忽略它。

如果没有忽略,当then()回调不为function时将会抛出异常,导致链式调用中断

  • 处理状态为resolve/reject的情况:其实我们上边 then() 的写法是对应状态为pending的情况,但是有些时候,resolve/reject 在 then() 之前就被执行(比如Promise.resolve().then()),如果这个时候还把then()回调push进resolve/reject的执行队列里,那么回调将不会被执行,因此对于状态已经变为fulfilledrejected的情况,我们直接执行then回调
// then方法,接收一个成功的回调和一个失败的回调
  then(resolveFn, rejectFn) {
    // 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
    typeof resolveFn !== 'function' ? resolveFn = value => value : null
    typeof rejectFn !== 'function' ? rejectFn = reason => {
      throw new Error(reason instanceof Error? reason.message:reason);
    } : null
  
    // return一个新的promise
    return new MyPromise((resolve, reject) => {
      // 把resolveFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
      const fulfilledFn = value => {
        try {
          // 执行第一个(当前的)Promise的成功回调,并获取返回值
          let x = resolveFn(value)
          // 分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
  
      // reject同理
      const rejectedFn  = error => {
        try {
          let x = rejectFn(error)
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
  
      switch (this._status) {
        // 当状态为pending时,把then回调push进resolve/reject执行队列,等待执行
        case PENDING:
          this._resolveQueue.push(fulfilledFn)
          this._rejectQueue.push(rejectedFn)
          break;
        // 当状态已经变为resolve/reject时,直接执行then回调
        case FULFILLED:
          fulfilledFn(this._value)    // this._value是上一个then回调return的值(见完整版代码)
          break;
        case REJECTED:
          rejectedFn(this._value)
          break;
      }
    })
  }

兼容同步任务

executor是异步任务:

Promise的执行顺序是new Promise -> then()收集回调 -> resolve/reject执行回调

executor是同步任务:new Promise -> resolve/reject执行回调 -> then()收集回调,resolve的执行跑到then之前去了,

为了兼容这种情况,给resolve/reject执行回调的操作包一个setTimeout,让它异步执行。

//Promise/A+规定的三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  // 构造方法接收一个回调
  constructor(executor) {
    this._status = PENDING     // Promise状态
    this._value = undefined    // 储存then回调return的值
    this._resolveQueue = []    // 成功队列, resolve时触发
    this._rejectQueue = []     // 失败队列, reject时触发

    // 由于resolve/reject是在executor内部被调用, 因此需要使用箭头函数固定this指向, 否则找不到this._resolveQueue
    let _resolve = (val) => {
      //把resolve执行回调的操作封装成一个函数,放进setTimeout里,以兼容executor是同步代码的情况
      const run = () => {
        if(this._status !== PENDING) return   // 对应规范中的"状态只能由pending到fulfilled或rejected"
        this._status = FULFILLED              // 变更状态
        this._value = val                     // 储存当前value

        // 这里之所以使用一个队列来储存回调,是为了实现规范要求的 "then 方法可以被同一个 promise 调用多次"
        // 如果使用一个变量而非队列来储存回调,那么即使多次p1.then()也只会执行一次回调
        while(this._resolveQueue.length) {    
          const callback = this._resolveQueue.shift()
          callback(val)
        }
      }
      setTimeout(run)
    }
    // 实现同resolve
    let _reject = (val) => {
      const run = () => {
        if(this._status !== PENDING) return   // 对应规范中的"状态只能由pending到fulfilled或rejected"
        this._status = REJECTED               // 变更状态
        this._value = val                     // 储存当前value
        while(this._rejectQueue.length) {
          const callback = this._rejectQueue.shift()
          callback(val)
        }
      }
      setTimeout(run)
    }
    // new Promise()时立即执行executor,并传入resolve和reject
    executor(_resolve, _reject)
  }

  // then方法,接收一个成功的回调和一个失败的回调
  then(resolveFn, rejectFn) {
    // 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
    typeof resolveFn !== 'function' ? resolveFn = value => value : null
    typeof rejectFn !== 'function' ? rejectFn = reason => {
      throw new Error(reason instanceof Error? reason.message:reason);
    } : null
  
    // return一个新的promise
    return new MyPromise((resolve, reject) => {
      // 把resolveFn重新包装一下,再push进resolve执行队列,这是为了能够获取回调的返回值进行分类讨论
      const fulfilledFn = value => {
        try {
          // 执行第一个(当前的)Promise的成功回调,并获取返回值
          let x = resolveFn(value)
          // 分类讨论返回值,如果是Promise,那么等待Promise状态变更,否则直接resolve
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
  
      // reject同理
      const rejectedFn  = error => {
        try {
          let x = rejectFn(error)
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
  
      switch (this._status) {
        // 当状态为pending时,把then回调push进resolve/reject执行队列,等待执行
        case PENDING:
          this._resolveQueue.push(fulfilledFn)
          this._rejectQueue.push(rejectedFn)
          break;
        // 当状态已经变为resolve/reject时,直接执行then回调
        case FULFILLED:
          fulfilledFn(this._value)    // this._value是上一个then回调return的值(见完整版代码)
          break;
        case REJECTED:
          rejectedFn(this._value)
          break;
      }
    })
  }
}

Promise.prototype.catch()

catch()方法返回一个Promise,并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。

//catch方法其实就是执行一下then的第二个回调
catch(rejectFn) {
  return this.then(undefined, rejectFn)
}

Promise.prototype.finally()

finally()方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。在finally之后,还可以继续then。并且会将值原封不动的传递给后面的then

//finally方法
finally(callback) {
  return this.then(
    value => MyPromise.resolve(callback()).then(() => value),             // MyPromise.resolve执行回调,并在then中return结果传递给后面的Promise
    reason => MyPromise.resolve(callback()).then(() => { throw reason })  // reject同理
  )
}
复制代码

MyPromise.resolve(callback())的意义:这个写法其实涉及到一个finally()的使用细节,finally()如果return了一个reject状态的Promise,将会改变当前Promise的状态,这个MyPromise.resolve就用于改变Promise状态,在finally()没有返回reject态Promise或throw错误的情况下,去掉MyPromise.resolve也是一样的

Promise.resolve()

Promise.resolve(value)方法返回一个以给定值解析后的Promise 对象。如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法)),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。

//静态的resolve方法
static resolve(value) {
  if(value instanceof MyPromise) return value // 根据规范, 如果参数是Promise实例, 直接return这个实例
  return new MyPromise(resolve => resolve(value))
}
复制代码

Promise.reject()

Promise.reject()方法返回一个带有拒绝原因的Promise对象。

//静态的reject方法
static reject(reason) {
  return new MyPromise((resolve, reject) => reject(reason))
}

Promise.all()

Promise.all(iterable)方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve)

如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果

//静态的all方法
static all(promiseArr) {
  let index = 0
  let result = []
  return new MyPromise((resolve, reject) => {
    promiseArr.forEach((p, i) => {
      //Promise.resolve(p)用于处理传入值不为Promise的情况
      MyPromise.resolve(p).then(
        val => {
          index++
          result[i] = val
          //所有then执行后, resolve结果
          if(index === promiseArr.length) {
            resolve(result)
          }
        },
        err => {
          //有一个Promise被reject时,MyPromise的状态变为reject
          reject(err)
        }
      )
    })
  })
}
复制代码

Promise.race()

Promise.race(iterable)方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

static race(promiseArr) {
  return new MyPromise((resolve, reject) => {
    //同时执行Promise,如果有一个Promise的状态发生改变,就变更新MyPromise的状态
    for (let p of promiseArr) {
      MyPromise.resolve(p).then(  //Promise.resolve(p)用于处理传入值不为Promise的情况
        value => {
          resolve(value)        //注意这个resolve是上边new MyPromise的
        },
        err => {
          reject(err)
        }
      )
    }
  })
}

Promise.all()哪怕一个请求失败了也能得到其余正确的请求结果的解决方案⭐⭐

await(串行):如果在一个async的方法中,有多个await操作的时候,程序会变成完全的串行操作,一个完事等另一个但是为了发挥node的异步优势,

当异步操作之间不存在结果的依赖关系时,可以使用promise.all来实现并行,all中的所有方法是一同执行的。

执行后的结果:

async函数中,如果有多个await关键字时,如果有一个await的状态变成了rejected,那么后面的操作都不会继续执行,promise也是同理

await的返回结果就是后面promise执行的结果,可能是resolves或者rejected的值使用场景循环遍历方便了代码需要同步的操作(文件读取,数据库操作等)
 

promise.all并行(同时)执行promise,当其中任何一个promise 出现错误的时候都会执行reject,导致其它正常返回的数据也无法使用

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');//setTimeout(function[, delay, arg1, arg2, ...]);
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
// Expected output: Array [3, 42, "foo"]

map的每一项都是promise,catch方法返回值会被promise.reslove()包裹,这样传进promise.all的数据都是resolved状态的。

let p1 = Promise.resolve(1)
let p2 = Promise.resolve(2)
let p3 = Promise.resolve(3)
let p4 = Promise.resolve(4)
let p5 = Promise.reject("error")
let arr = [p1,p2,p3,p4,p5];

let all = Promise.all(arr.map((promise)=>promise.catch((e)=>{console.log("错误信息"+e)})))
all.then(res=>{console.log(res)}).catch(err=>console.log(err));

Mypromise

//Promise/A+规范的三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  // 构造方法接收一个回调
  constructor(executor) {
    this._status = PENDING     // Promise状态
    this._resolveQueue = []    // 成功队列, resolve时触发
    this._rejectQueue = []     // 失败队列, reject时触发

    // 由于resolve/reject是在executor内部被调用, 因此需要使用箭头函数固定this指向, 否则找不到this._resolveQueue
    let _resolve = (val) => {
      if(this._status !== PENDING) return   // 对应规范中的"状态只能由pending到fulfilled或rejected"
      this._status = FULFILLED              // 变更状态

      // 这里之所以使用一个队列来储存回调,是为了实现规范要求的 "then 方法可以被同一个 promise 调用多次"
      // 如果使用一个变量而非队列来储存回调,那么即使多次p1.then()也只会执行一次回调
      while(this._resolveQueue.length) {    
        const callback = this._resolveQueue.shift()
        callback(val)
      }
    }
    // 实现同resolve
    let _reject = (val) => {
      if(this._status !== PENDING) return   // 对应规范中的"状态只能由pending到fulfilled或rejected"
      this._status = REJECTED               // 变更状态
      while(this._rejectQueue.length) {
        const callback = this._rejectQueue.shift()
        callback(val)
      }
    }
    // new Promise()时立即执行executor,并传入resolve和reject
    executor(_resolve, _reject)
  }

  // then方法,接收一个成功的回调和一个失败的回调
  then(resolveFn, rejectFn) {
    this._resolveQueue.push(resolveFn)
    this._rejectQueue.push(rejectFn)
  }
}

【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理) - 掘金

fetch(ES6 拉取网络资源)

以前发送请求,使用ajax或者axios,现在还可以使用fetch。这个是window自带的方法,和xhr是一个级别的。(xhr=new XMLHttpRequest())

fetch(input[, init]);

// url (必须), options (可选)
fetch('/some/url', {method: 'get'})

.then(function(response) {

})

.catch(function(err) {
    // 出错了;等价于 then 的第二个参数,但这样更好用更直观 :(

});

第二个then接收的才是后台传过来的真正的数据

window.fetch(url, { method: 'get'})   //fetch() 返回响应的promise 
    // 第一个then  设置请求的格式
        .then(res => return res.json())  // 第一层then返回的是:响应的报文对象  
        .then((data) => {             //第二层:如果想要使用第一层的数据,必须把数据序列化  
                                       res.json() 返回的对象是一个 Promise对象再进行then()
         <!-- data为真正数据 -->
    }).catch(e => console.log("Oops, error", e))

Fetch()方法介绍_小钢炮vv的博客-CSDN博客_fetch

 async/await函数

异步函数实际上原理与 Promise 原生 API 的机制是一模一样的,只不过更便于阅读。

相当于promise用法的语法糖,async/await实际上是对Generator(生成器)的封装

ES6 引入了 Generator 函数,被ES7 提出的async/await取代了,

将Generator函数的星号 * 替换成async,将yield替换成await。

相对于Generator函数的改进:自带执行器,会自动执行。

await规定了异步操作只能一个一个排队执行,从而达到用同步方式,执行异步操作的效果

Promise.resolve(a)
  .then(b => {
    // do something
  })
  .then(c => {
    // do something
  })

//等价于
async () => {
  const a = await Promise.resolve(a);
  const b = await Promise.resolve(b);
  const c = await Promise.resolve(c);
}

async关键字+函数,表明该函数内部有异步操作

函数返回的是一个状态为fulfilledPromise对象

如果结果是,会经过Promise包装返回。
如果是promise则会等待promaise 返回结果,否则,就直接返回对应的值,

await 操作符+promise对象用于组成表达式

awai+,就会转到一个立即resolve的Promise对象。

async function asyncFunc() {
    let value = await new Promise(
        function (resolve, reject) {
            resolve("Return value");
        }
    );
    console.log(value);
}
asyncFunc();

await只能在async函数中出现, 普通函数直接使用会报语法错误SyntaxError

await语句后的Promise对象变成reject状态时,那么整个async函数会中断,后面的程序不会继续执行

处理异常的机制将用 try-catch 块实现

async function asyncFunc() {
    try {
        await new Promise(function (resolve, reject) {
            throw "Some error"; // 或者 reject("Some error")
        });
    } catch (err) {
        console.log(err);
        // 会输出 Some error
    }
}
asyncFunc();

async和await的讲解_傲娇的koala的博客-CSDN博客_async await

跨域通信⭐⭐⭐

所谓同源(域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能

如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。(跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。)

同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。

服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。)

服务器与服务器之间是可以通信的不受同源策略的影响:Nginx反向代理,proxy代理

同源策略限制内容有:

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM 节点
  • AJAX 请求发送后,结果被浏览器拦截了

允许跨域加载资源的标签:

  • script标签的跨域功能:<img src=XXX><script src=XXX>
  • 规定外部脚本文件的 URL:<link href=XXX>

跨域的解决方案思路两种,绕过去和cors;

  • iframe方式可传递数据,但组织和控制代码逻辑太复杂,鸡肋;
  • jsonp适合加载不同域名的js、css,img静态资源,现在浏览器兼容性高了,以及受限于仅get方式,逐步淘汰了;
  • Nginx反向代理和nodejs中间件跨域原理都相似,是绕过去的方式,是从古至今通用的没完解决方案,适合前后端分离的前端项目调后端接口。都是搭建一个服务器,直接在服务器端请求HTTP接口,缺点也许是服务器压力大一点,实际中那点压力根本不是大问题;同时反向代理更适合内部应用间访问和共享;
  • cors才是真正的称得上跨域请求解决方案(支持所有类型的HTTP请求,但浏览器IE10以下不支持)适合做ajax各种跨域请求;
  • websocket都是HTML5新特性兼容性不是很好,只适用于主流浏览器和IE10+。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/627390.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Revit中绘制多坡度的迹线屋顶和构件对齐

一、Revit中创建特殊多坡度的迹线屋顶 在我们的日常生活中可以见到一些建筑屋顶为偏欧式风格的屋顶&#xff0c;而有时候在做迹线屋顶时也需要作出如图一所示的效果&#xff0c;说明特殊的多坡度屋顶也是应用非常广泛的&#xff0c;那我们应该如何实现绘制呢? 1.要得到如上图所…

Linux系统:安装及管理程序

安装及管理程序 一、linux源码包&#xff1a;1.源码包&#xff1a;2.二进制包&#xff1a;3.源码包的好处&#xff1a;4.源码包不足&#xff1a; 二、编译安装的过程&#xff1a;1.重点步骤&#xff1a; 三、挂载1.格式&#xff1a;2.挂载规则&#xff1a; 四、应用程序和系统命…

使用Jmeter进行性能测试的这套步骤,涨薪2次,升职一次

项目背景&#xff1a; 我们的平台为全国某行业监控平台&#xff0c;经过3轮功能测试、接口测试后&#xff0c;98%的问题已经关闭&#xff0c;决定对省平台向全国平台上传数据的接口进行性能测试。 01、测试步骤 1、编写性能测试方案 由于我是刚进入此项目组不久&#xff0c…

2023年专业连锁行业研究报告

第一章 行业概况 专业连锁行业是指以连锁经营模式运营的公司&#xff0c;其主要业务涵盖零售、餐饮、酒店、医疗、教育等领域。这些公司通过规模化、标准化的经营模式和供应链管理&#xff0c;提供专业化、高质量的产品和服务。专业连锁行业在全球范围内蓬勃发展&#xff0c;并…

LeetCode - 1 两数之和

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出和为目标值 target 的那 两个整数&#xff0c;并返回它们…

Jmeter接口测试实战篇:10分钟学会Jmeter的用法

一提到接口测试&#xff0c;通常大家会有这样的疑问&#xff1a;前端测试不是已经覆盖到各种业务逻辑了吗&#xff1f;为什么还要做接口测试&#xff0c;接口测试和前端测试是不是重复了&#xff1f;对于这个问题&#xff0c;可以从下面几个方面来解释&#xff1a; 什么是接口…

架构之冷热分离

本文依据《从程序员到架构师》阅读有感&#xff0c;记录书中案例并且结合作者工作经历进行分析。 当数据量过大&#xff0c;业务查询慢甚至导致数据库服务器CPU飙升&#xff0c;导致数据库宕机&#xff0c;影响用户体验。 场景&#xff1a; 1.客户两年多产生了近2000万的工单…

k聚类简单实现(灰度分割,分黑白)

加载图像&#xff1a; k聚类分割后图像&#xff0c;分成黑白两类&#xff0c;故意把结果黑色类染红&#xff0c;核对发现是正确的&#xff1a; 具体算法如下&#xff1a; float globu1 40; float globu2 180; public void k均值迭代法更新(int imgw, int imgh, byte…

云晶-新一代云上操作系统的新定义,价值,应用范围

本文&#xff0c;从人类社会信息化到数字化的演变过程&#xff0c;以及当前的企业数字化现状&#xff0c;并回顾信息技术的几次革命来阐述总结操作系统的价值和意义。我们基于行业发展规律&#xff0c;重新定义了云晶-云上操作系统的架构和建设要点。并给出了大胆设想。 您也许…

大模型部署实战(一)——Ziya-LLaMA-13B

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

【TA100 】图形 2.1 色彩空间介绍

->如何描述色彩 不要感性的认知&#xff0c;我们来 正经讨论&#xff0c;探究问题的本质- - ->色彩科学。 一、色彩发送器&#xff08;光源发射角度&#xff09; ->以面向对象的思想来理解&#xff1a; ● 出生点&#xff1a;光源 ● Object&#xff1a;射线&#…

15 【Vue-Router】

1.相关理解 1.1 vue-router 的理解 vue的一个插件库&#xff0c;专门用来实现SPA应用 1.2 对SPA应用的理解 1.单页Web应用&#xff08;single page web application&#xff0c;SPA&#xff09; 2.整个应用只有一个完整的页面 3.点击页面中的导航链接不会刷新页面&#xff…

STM32任务调度

目录 什么是任务调度&#xff1f; FreeRTOS的任务调度规则是怎样的&#xff1f; 抢占式调度运行过程 时间片调度运行过程 任务的状态 任务综合小实验 实验需求 cubeMX配置 什么是任务调度&#xff1f; 调度器就是使用相关的调度算法来决定当前需要执行的哪个任务。 Fr…

编程新手如何提高编程能力?

如果刚开始写代码时能读一读《整洁代码的艺术》那是个不错的选择。这本书会告诉您&#xff0c; 如何应用九大原则来提高编程能力。 良好的编程技能带来更整洁的代码&#xff0c; 让您更专注、更有效地利用时间&#xff0c;得到更高质量的结果。只要应用本书中提到的那些原则&am…

springboot集成rabbitmq

简介 RabbitMQ 是实现 AMQP&#xff08;高级消息队列协议&#xff09;的消息中间件的一种&#xff0c;最初起源于金融系统&#xff0c;用于在分布式系统中存储转发消息&#xff0c;在易用性、扩展性、高可用性等方面表现不俗。 RabbitMQ 主要是为了实现系统之间的双向解耦而实…

【LeetCode】HOT 100(4)

题单介绍&#xff1a; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识算法与数据结构的新手和想要在短时间内高效提升的人&#xff0c;熟练掌握这 100 道题&#xff0c;你就已经具备了在代码世界通行的基本能力。 目录 题单介绍&#…

容器(第三篇)docker-cgroup资源限制

Docker 通过 Cgroup 来控制容器使用的资源配额&#xff0c;包括 CPU、内存、磁盘三大方面&#xff0c; 基本覆盖了常见的资源配额和使用量控制。 Cgroup 是 ControlGroups 的缩写&#xff0c;是 Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如 CPU、内存、…

一路狂飙,性能测试流程与性能测试主要指标整理,直接上高速...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能测试实战 性…

什么是真正的需求,如何才能找到?

此为内容创作模板&#xff0c;在发布之前请将不必要的内容删除 对需求本身的误判&#xff0c;比错误本身更为恐怖&#xff0c;直接导致必然失败的局面。 工作失误必不可免&#xff0c;好工作核心在于有需求&#xff0c;自己需要去做&#xff0c;有动力&#xff0c;别人需要你…

【Cloudgetway网关】 GetWay网关入门使用

一、概述 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ow0KO8iV-1686286922163)(null)] GateWay是zuul的替代品&#xff0c;由于Zuul2.0迟迟没有出来&#xff0c;SpringCloud社区推出了gateWay网关来替代zuul1.x版本。提供了以下功能: 底层使用n…