AJAX和Promise

裸辞休息一段时间以后,整理一些八股内容,重新投简历。

参考:GitHub - Rox-Cat/=  

AJAX

Ajax概念相关

1. 什么是Ajax?它的作用是什么?

概念:Ajax是异步JS和XML的缩写,它是一种动态创建网页的技术。

作用:它能够在不刷新整个页面的情况下,通过JS异步向服务器请求数据,异步加载和更新部分页面内容,说提高用户体验,动态加载数据,表单验证和提交等。

Ajax一般使用XmlHttpRequest来实现异步通信,也可以使用Fetch API或者JQuery来实现。

2. 传统的请求方式?与Ajax有什么不同?

传统的请求方式:直接在浏览器地址栏上输入URL,点击超链接、提交form表单、通过JS代码发送请求。

不同之处:

使用传统请求导致用户体验有空白期(用户的体验是不连贯的),用户体验很差。

而使用Ajax是异步请求,不需要刷新整个页面来更新页面内容,可以实现局部更新。而且页面更新时,不会发生阻塞,可以先执行后面的内容,等更新到数据时,再进行局部更新,使得用户的体验极好,并减少了服务器的压力。

3. Ajax常用于的场景有哪些?

提高用户的体验:通过异步加载数据,不需要刷新整个页面来更新内容,只需要局部更新即可,减少页面加载的时间。

动态加载数据:通过Ajax请求数据,可以动态加载页面数据,实现一些交互效果,例如:搜索建议,下拉更新。

表单验证和提交:通过Ajax验证表单数据的合法性,异步提交数据,避免了页面重新加载。

4. 如何理解Ajax的异步?

Ajax的异步体现在他的请求方式上。在传统的请求方式中,当点击一个链接或者提交表单时,浏览器会向服务器请求数据,并等待服务器返回响应后才更新页面。这种请求方式需要重新加载整个页面,且需要等一段时间后才能看到更新的页面内容。

而在Ajax中,Ajax使用XmlHttpRequest对象来实现异步请求,当浏览器发送Ajax请求时,页面不会阻塞,会继续执行后面的代码。当服务器返回数据数据时,浏览器会通过回调函数 处理响应数据,并局部更新页面内容。这种方式可以实现异步更新,提高用户体验和页面的响应速度。

5. Ajax优缺点

优点:通过Ajax异步请求方式,当浏览器向服务器发送请求时,页面不会发生阻塞,可以继续执行后面的代码。当服务器返回数据时,浏览器会通过回调函数处理响应数据,并局部更新页面内容。这种方式提高了用户体验、提高页面的响应速度,减少了服务器的负担和减少了网络流量。

缺点:

安全问题:Ajax暴露了与服务器交互细节;开发复杂程度高;不容易调试;破坏了程序异常机制;对于对JS支持不好的浏览器不友好;对搜索引擎不友好;

6. AJAX最大的特点是什么。

通过Ajax异步请求方式,当浏览器向服务器发送请求时,页面不会发生阻塞,可以继续执行后面的代码。当服务器返回数据时,浏览器会通过回调函数处理响应数据,并局部更新页面内容。这种方式提高了用户体验、提高页面的响应速度,减少了服务器的负担和减少了网络流量。

7. 关于Ajax的真实应用

动态加载数据、实时搜索、表单提交、图片上传、聊天应用程序等

Ajax相关面试题

1. 什么是Ajax?它的作用是什么?

2. 为什么要用Ajax?(简述Ajax的优点)

3. AJAX最大的特点是什么。

4. 请介绍一下XMLHttprequest对象。

Ajax的核心是JS对象XmlHttpRequest,它是一种支持异步请求技术。XmlHttpRequest对象可以使你使用Js向服务器发送请求并处理响应,而不阻塞用户。通过XmlHttpRequest对象可以在页面加载以后对页面进行局部更新。

5、AJAX技术体系的组成部分有哪些。

Html,Css,Js,XmlHttpRequest,Dom,Xml

6.原生js ajax请求有几个步骤?分别是什么?

//创建XMLHttpRuqest对象
var ajax = new XMLHttpRequest()
//规定请求的类型、URL以及是否异步处理请求
ajax.open('GET',url, true)
//发送信息到服务器时内容编码类型
ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded");
//发送请求
ajax.send(null);
//接受服务器响应数据
ajax.onreadystatechange = function () {
  if (obj.readyState == 4 && (obj.status == 200 || obj.status == 304)) {
    
  }
};

7. json字符串转换为json对象、json对象转换json字符串

//字符串转对象
1、JSON.parse(arr) //会检查给出的字符串是否符合字符串格式,不符合就会报错,不回去执行。
2、eval('(' + arr + ')') //不会检查给的字符串是否符合JSON格式,如果给出的字符串出现JS代码,也会执行。相对而言eval是不安全的,一般使用JSON.parse()。
//对象转字符串
1、JSON.stringify(obj)

8.什么是JSON?

一种轻量级的数据交换格式

9. ajax几种请求方式?他们的优缺点?

请求方式:post,get,delete,put

优缺点:

代码上的区别:

get通过url传递参数

post设置请求头,规定请求数据类型,参数在请求体中

使用上的区别:

(1)post比get更安全

因为get的参数在url中,post的参数在请求体中

(2)get传输速度比post更快,根据传参决定的

post是通过请求体传参,后台通过数据流接受,速度稍微慢一些。而get通过url传参,可以直接会去。

(3)post传输文件大小理论上没有限制,而get有

(4)get获取数据而post上传数据

10. 什么情况造成跨域?

同源策略限制不同源会造成跨域,而协议、域名和端口号只要有一个不同就是不同源

11. 跨域解决方案有哪些?

(1)动态创建一个script标签

原理:利用script标签的src和href属性不受同源策略限制,可以请求第三方服务器数据内容。

步骤:

//去创建一个script标签
var script = document.createElement("script");
//script的src属性设置接口地址,并带一个callback回调函数名称
script.src = "http://127.0.0.1:8888/index.php?callback=jsonCallback";
//插入页面
document.head.appendChild(script);
//通过定义函数名去接收后台返回的数据
function jsonCallback(data)
{
  // 返回的数据是json对象,可以直接使用
  //ajax 取得的数据是json字符串,则需要转化为json对象才可以使用
}

(2)CORS:跨域资源共享

CORS全称Cross-Origin Resource Sharing,是一种W3C标准,主要是通过设置响应头来实现跨域请求。

(3)反向代理

在同域下搭建一个代理服务器,让代理服务器去请求目标服务器的数据,然后再将数据返回给前端。

(4)Window + iframe

12.http常见状态码有哪些

(1)2开头的状态码

2xx表示成功处理了请求、

(2)3开头的状态码

3xx(重定向):表示完成请求需要进一步的操作。

304(未修改):自上次请求后,请求的页面未修改过。

(3)4开头的状态码

4xx(请求错误):表示请求可能出错,妨碍了服务器处理

400(错误请求):服务器不理解请求的语法。

403(禁止):服务器拒绝请求

404(未找到):服务器找不到请求的网页

(4)5开头的状态码

5xx(服务器错误):服务器在处理请求时发生内部错误,是服务器本身的错误而非请求错误。

500(服务器内部错误):服务器在处理请求时发生内部错误,是服务器本身的错误而非请求错误

13. 介绍一下XMLHttpRequest对象的常用方法和属性

open("method", "url"):建立对服务器的调用,第一个参数为HTTP请求,第二个参数为请求页面URL。

send()方法:发送具体请求。

abort()方法:停止当前请求

readyState属性:表示请求的状态,有五个可取值,0为未初始化,1为正在加载,2为已加载,3为交互中,4为完成

responseText属性:服务器的响应,表示为一个串

responseXML属性:服务器响应,表示为XML

status:服务器的HTTP状态码,200对于OK,400对于not found

14. 什么是XML

扩展标记语言,能用一系列简单的标记描述数据

15. AJAX都有哪些优点和缺点?

Axios

Axios基础

1. 什么是Axios(Axios简介)

axios是基于promise的HTTP库,可以用在浏览器和node.js中。

2. Axios发送请求

//发起一个post请求
axios({
  method:'post',
  url: '/user/123456'
  data: {
    xx: xxx,
    xx: xxx
  }
})

3. Axios别名发送请求

(1)Get请求

基本使用:axios.get(url, config).then().catch()

配置项:

axios.get("http://localhost:3000/users", {
  params:{
    xx: "xx"
  }
}).then(res => {
  console.log(res)
}).catch(error => {
  coanole.log(error)
});

(2)post请求

基本使用:axios.post(url, config).then().catch()

配置项:

axios.post("http://localhost:3000/users", {
  xx: "xx",
  xx: "xx"
}).then(res => {
  console.log(res)
}).catch(error => {
  coanole.log(error)
});

(3)delete请求  

axios.delete("http://localhost:3000/users").then(res => {
  console.log(res)
}).catch(error => {
  coanole.log(error)
});

(4)put请求

axios.put("http://localhost:3000/users", {
  xx: "xx",
  xx: "xx"
}).then(res => {
  console.log(res)
}).catch(error => {
  coanole.log(error)
});

4. Axios实例发送请求

使用多个axios,需要配置url.header.type等等,那么就需要重复写多个配置。对此解决的办法如下:

//创建实例
const instance = axios.create({
  baseURL: 'xxxx',
  timeout: 1000,
  header: {'X-Custom-Header': 'footbar'}
});
//使用实例
//方法一
instance({
  url: '/post'
});
//方法二
instance.get('/post');

5. 请求配置的内容

config:有哪些内容?

常用的有method,url,params,data,baseURL,timeout,header

6. 响应体结构

//data 由服务器提供的响应
data: {},

// status 来自服务器响应HTTP状态码
status: 200,

//statusText 来自服务器响应的HTTP状态信息
statusText: 'OK',

//headers 服务器响应头
headers: {},

//config 是axios请求配置信息
config: {},

//request 是生成此响应的请求
request: {}

7. 默认配置

Axios高级

1. Axios拦截器

拦截器的作用:axios的拦截器用于发送请求或响应时,拦截并对其做预处理或处理。通过设置请求拦截器响应拦截器,可以在请求发起前和响应返回后做相关处理。

使用拦截器:axios提供了axios.interceptor属性设置拦截器,axios.interceptor.request属性设置请求拦截器,axios.interceptor.response属性设置响应拦截器。可以通过use()添加拦截器函数,也可以通过eject()移除拦截器。

2. Axios.all

https://juejin.cn/post/7067827033908183053

Axios原理

1. Axios原理

回答思路:

(1)总体介绍Axios

axios是基于promise的HTTP库,可以在浏览器和node.js中使用

(2)介绍Axios构造函数

axios的核心是Axios构造函数,他可以创建一个axios实例对象,该对象有一个default属性,用于存放配置对象;还有一个interceptors属性,用于设置请求和响应拦截器。

(3)介绍Axios.prototype.request方法

(4)介绍Axios拦截器原理

2. Axios发送请求的原理

在浏览器端通过xhr发送请求

在Node.js通过HTTP模块发送请求

3. Aixos拦截器

4. 取消Axios请求

Axios 可以通过使用 CancelToken 来实现取消请求的功能。

步骤:

//创建一个 CancelToken 对象,用来存储取消请求的方法和标识:
const source = axios.CancelToken.source();

//在发送请求时,将 CancelToken 对象的 signal 属性作为参数传递给 axios:
axios.get('/foo/bar', {
  signal: source.signal
})
.then(function (response) {
  // 处理响应数据
})
.catch(function (error) {
  // 处理错误
});

//在需要取消请求的时候,调用 CancelToken 对象的 abort 方法:
source.abort('Operation canceled by the user.');

axios 就会中断请求,并抛出一个错误对象,其中包含了取消请求的原因。

取消时机:

需要注意的是,只有在请求还没有到达服务器的时候,才能取消请求。如果请求已经到达服务器,那么服务器会继续处理请求,并返回响应数据。这时候,即使客户端取消了请求,也无法阻止服务器的响应。所以,取消请求只能用来节省客户端的资源和带宽,并不能影响服务器的行为。

5. Axios二次封装

为什么要进行二次封装:

Axios二次封装是指根据项目的需求,对axios的请求和响应进行自定义配置,以提高代码的复用性和可维护性。例如,可以设置请求的基础URL,超时时间,这样我们在发送请求的时候就不需要每次都配置相同的内容。使用拦截器让我们对发送请求之前对于请求头统一的添加内容,使用响应拦截器可以实现预处理响应数据。

如何进行得二次封装:

//第一步:利用axios对象的create方法,去创建axios实例(其他的配置:基础路径、超时的时间)
const request = axios.create({
  //基础路径
  baseURL: import.meta.env.VITE_APP_BASE_API, //基础路径上会携带/api
  timeout: 5000, //超时的时间的设置
})

//第二步:request实例添加请求拦截器
request.interceptors.request.use((config) => {
  //获取用户相关的小仓库:获取仓库内部token,登录成功以后携带给服务器
  const userStore = useUserStore()
  if (userStore.token) {
    config.headers.token = userStore.token
  }
  //config配置对象,headers属性请求头,经常给服务器端携带公共参数
  //返回配置对象
  return config
})

//第三步:响应拦截器
request.interceptors.response.use(
  (response) => {
    console.log(response)
    //成功回调
    //简化数据
    return response.data
  },
  (error) => {
    //失败回调:处理http网络错误的
    //定义一个变量:存储网络错误信息
    let message = ''
    //http状态码
    const status = error.response.status
    switch (status) {
      case 401:
        message = 'TOKEN过期'
        break
      case 403:
        message = '无权访问'
        break
      case 404:
        message = '请求地址错误'
        break
      case 500:
        message = '服务器出现问题'
        break
      default:
        message = '网络出现问题'
        break
    }
    //提示错误信息
    ElMessage({
      type: 'error',
      message,
    })
    return Promise.reject(error)
  },
)

6. Axios的特点(Axios的概念)

7. 如果没有Axios,Ajax发送请求的过程

如果没有Axios,你可以使用原生JavaScript中的XMLHttpRequest对象来发送AJAX请求。

8. Axios,Ajax,fetch的关系区别

axios 和 ajax 都是对XMLHttpRequest这个对象的封装;而 fetch 则是window下的一个方法,是一个更底层的方法。

Ajax:是异步JS和XML的缩写,它是一种动态创建网页的技术。

Axios:是基于promise的HTTP库,可以用在浏览器和node.js中。

Fetch:使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch函数就是原生js,没有使用XMLHttpRequest对象。

XHR

XHR的概念

1. XMLHttprequest是什么?做什么?怎么用?

XMLHttpRequest对象是JavaScript中的一个内置对象,用于在客户端与服务器之间发送HTTP请求和接收响应,实现异步通信。通过XMLHttpRequest对象,可以向服务器发送请求,获取服务器返回的数据,并在不刷新整个页面的情况下更新页面内容。

2. 使用XHR发起GET请求的步骤

(1)创建 xhr 对象

(2)调用 xhr.open() 函数

(3)调用 xhr.send() 函数

(4)监听 xhr.onreadystatechange 事件

Fetch

1. Fetch简介

Fetch 是用于网络请求的 JavaScript API,可以替代传统的 XMLHttpRequest (XHR) 对象。它可以发送各种类型的请求,并且支持 Promise,使得异步操作更加方便和可读性更好。实现Ajax的一种手段!Fetch的存在减少了原生Ajax的使用。

2. Fetch基本用法方法

fetch("http://localhost:3000/users")
            .then(res=>res.json())
            .then(res=>{
                console.log(res)
            })


fetch("http://localhost:3000/users",{
            method:"POST",
            headers:{
                "content-type":"application/json"
            },
            body:JSON.stringify({
                username:"kerwin",
                password:"123"
            })
        })
            .then(res=>res.json())
            .then(res=>{
                console.log(res)
            })

fetch("http://localhost:3000/users/5",{
            method:"PUT",
            headers:{
                "content-type":"application/json"
            },
            body:JSON.stringify({
                username:"kerwin",
                password:"456"
            })
        })
            .then(res=>res.json())
            .then(res=>{
                console.log(res)
            })

fetch("http://localhost:3000/users/5",{
            method:"DELETE"
        })
            .then(res=>res.json())
            .then(res=>{
                console.log(res)
            })

Promise

异步任务的实现方式

1. 回调函数(Callback)

回调函数是最基本的异步处理方式,它允许你将一个函数作为参数传递给另一个函数。当外部函数完成异步操作后,内部的回调函数就会被调用。

2. 事件监听(Event Listeners)

事件监听是另一种处理异步任务的方法。你可以为特定的事件注册一个监听器(也是一个函数)。当事件发生时(通常是在异步操作完成时),这个监听器会被自动调用。

3. 发布/订阅模式(Publish/Subscribe)

发布/订阅模式或者说观察者模式,涉及到订阅者和发布者两类对象。订阅者订阅特定的事件,并注册回调函数。当发布者发布事件时,所有订阅了该事件的订阅者注册的回调函数都会被调用。

Promise概念相关

1. promise的概念

Promise 是一种用于异步编程的解决方案,它的本质是对回调函数的封装。它可以让异步操作更加清晰、简洁,避免了回调地狱的问题

异步编程:

异步编程是一种编程模式,其中操作不会立即返回结果,而是在后续的某个时刻返回。异步编程通常用于处理一些需要等待的操作,例如读取文件、发送网络请求、执行定时器等。

回调地狱:

回调地狱是一种由于嵌套过多的回调函数而导致代码可读性和可维护性降低的现象。当有多个异步操作需要依次执行时,为了保证它们的执行顺序,往往需要将后续的操作嵌套在前面操作的回调函数中。这种嵌套的回调函数可能会变得非常深,难以维护和理解,从而形成回调地狱。

2. promise状态变化

当我们创建promise实例的时候,会传入一个函数,函数的参数的为resolve和reject,初始时,promise实例的状态为pending,当resolve函数执行是状态变为fulfilled,当reject函数执行,状态变为rejected。

3. promise提供了一些方法

(1)then方法可以处理promise状态为fullfilled和rejected

(2)catch用来捕获rejected,执行相应的函数

rejected:我们可以理解为Promise出现throw new Error,那么该promise对象就是rejected,而不是抛出到全局;如果没有接收该Error,就会在全局展示

(3)finally不管是什么状态都会执行

finally:如果没错,就返回上一个状态的promise,如果有错误/rejected,就抛出错误

(4)Promise.allSettled

Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。

(5)Promise.any

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

4. Promise中回调函数是同步的还是异步的?

是同步的

5. then的链式调用是同步的还是异步的?

异步的,会加入微任务队列

6. Promise 的状态转移

由 new Promise 构造器返回的 promise 对象具有以下内部属性:

  • state —— 最初是 "pending",然后在 resolve 被调用时变为 "fulfilled",或者在 reject 被调用时变为 "rejected"
  • result —— 最初是 undefined,然后在 resolve(value) 被调用时变为 value,或者在 reject(error) 被调用时变为 error

当Promise实例化后处于pending状态,表示异步操作还未完成;

当异步操作完成后,如果操作成功,则状态转移为fulfilled,同时result会变为操作成功的结果;

如果操作失败,则状态转移为rejected,同时result会变为操作失败的原因。

在状态转移后,Promise的状态和result值都是不可变的。

7. then()函数的执行与返回

8. async 和 await相关的执行顺序

等价于 把他看做是promise 的链式调用,注意await不是new promise,而是then()的回调函数,因为我们通过返回值来确定promise的状态而不是要依据resolve和rejected来决定。

改写async

//原码
console.log(2); 
async function s1() { 
    console.log(7) 
    await s2(); 
    console.log(8); 
} 
asycn function s2() {
    console.log(9); 
} 
s1(); 
console.log(5); 这段函数的返回值

//改写后
console.log(2);
function s1() {
    console.log(7)
    return s2().then(() => {
        console.log(8);
    });
}
function s2() {
    console.log(9);
    return Promise.resolve();
}
s1().then(() => {
    console.log(5);
});

执行顺序比较明确:2, 7, 9, 5, 8

  • 首先,整体代码是一个宏任务,它开始执行。
  • console.log(2) 打印出 2。
  • s1() 函数被调用,它返回一个 promise,并且把 console.log(5) 作为回调函数放入微任务队列中。
  • s1() 函数内部,console.log(7) 打印出 7。
  • s2() 函数被调用,它返回一个 promise,并且把 console.log(8) 作为回调函数放入微任务队列中。
  • s2() 函数内部,console.log(9) 打印出 9。
  • 整体代码结束,开始清空微任务队列。
  • console.log(8) 被执行,打印出 8。
  • console.log(5) 被执行,打印出 5。
  • 微任务队列清空完毕。

9. async和await中的捕获错误

await返回的是一个rejected的

要判断 await 返回的 Promise 的状态,有两种方法:

  • 一种是使用 try/catch 语句,如果 await 的 Promise 被拒绝(rejected),那么 catch 块会捕获到拒绝的原因,并抛出异常。例如:
try {
  // Do some asynchronous operation that may throw an exception
  await someAsyncOperation();
}
catch (error) {
  // Handle the error
}
  • 另一种是使用 .then() 和 .catch() 方法,如果 await 的 Promise 被履行(resolved),那么 .then() 方法会接收到履行的值,并执行相应的回调函数。如果 await 的 Promise 被拒绝,那么 .catch() 方法会接收到拒绝的原因,并执行相应的回调函数。例如:
await someAsyncOperation()
.then(value => {
  // Handle the resolved value
})
.catch(error => {
  // Handle the rejected reason
});

这两种方法都可以用来判断 await 返回的 Promise 的状态,但是有一些区别:

  • try/catch 可以捕获所有异常,包括语法错误或类型错误等。.then() /.catch 只能捕获异步方法中 reject 的错误。
  • try/catch 可以在一个块中处理多个 await 表达式的异常,而 ..then() /.catch 需要为每个 await 表达式单独处理。
  • try/catch 更符合同步代码的风格,而 ..then() /.catch 更符合异步代码的风格。

10. 在promise的使用中,如果.then()里面只有一个函数,但是.then之前的promise的状态是rejected,他会怎么办?

根据我搜索到的信息,如果.then()里面只有一个函数,但是.then之前的promise的状态是rejected,那么这个函数不会被调用,而是会跳过这个.then(),直到找到下一个有onRejected参数的.then()或者.catch()。如果没有找到任何处理rejected状态的回调函数,那么这个promise就会一直保持rejected状态,并且抛出错误。你可以使用.catch()方法来捕获并处理错误。

11. 如果.then(xx).then()如果第一个.then没有返回值,那第二个.then接受到的result是什么?

根据我搜索到的信息,如果.then()没有返回值,那么它会返回一个undefined的值,这个值会作为下一个.then()的参数。也就是说,第二个.then()接收到的result是undefined。如果你想要传递一个有意义的值给下一个.then(),你需要在第一个.then()里面使用return语句

12. promise.all()

1、Promise.all方法可以把多个promise实例包装成一个新的promise实例;
Promise.all([promise1,promise2]):Promise---最终返回Promise实例;
2.全部加载成功 则返回所有promise实例中resolve()回来带的参数,按数组中一一对应的顺序所集合的数组
若任意有一个失败 ,立即决议失败,将失败的promise实例(reject()中参数)传递给我们;
3.若Promise.all([ ])中,数组为空数组,则立即决议为成功执行resolve( );

数组项全部决议为成功时:

 function getData1(){
                return new Promise( (resolve,reject) => {
                    setTimeout( () => {
                        console.log('第一条数据加载成功');
                        resolve('data1');
                    },1000)
                })
            }
            
            function getData2(){
                return new Promise( (resolve,reject) =>{
                    setTimeout( () => {
                        console.log('第二条数据加载成功');
                        resolve('data2');
                    },1000)
                })
            }
            
            function getData3(){
                return new Promise( (resolve,reject) => {
                    setTimeout( () => {
                        console.log('第三条数据加载成功');
                        resolve('data3');
                    },1000)
                })
            }
            
            function getData4(){
                return new Promise( (resolve,reject) => {
                    setTimeout( () => {
                        console.log('第四条数据加载成功');
                        resolve('data4')
                    },200)
                })
            }
            
            let p = Promise.all( [getData1(), getData2(), getData3(), getData4() ])
            
            p.then(arr => {
                console.log(arr)  // ['data1', 'data2', 'data3', 'data4']
            }, e => {
                console.log(e)
            })

数组项有一项决议为失败时:

function getData1(){
                return new Promise( (resolve,reject) => {
                    setTimeout( () => {
                        console.log('第一条数据加载成功');
                        resolve('data1');
                    },1000)
                })
            }
            
            function getData2(){
                return new Promise( (resolve,reject) =>{
                    setTimeout( () => {
                        console.log('第二条数据加载成功');
                        resolve('data2');
                    },1000)
                })
            }
            
            function getData3(){
                return new Promise( (resolve,reject) => {
                    setTimeout( () => {
                        console.log('第三条数据加载成功');
                        resolve('data3');
                    },1000)
                })
            }
            
            function getData4(){
                return new Promise( (resolve,reject) => {
                    setTimeout( () => {
                        console.log('第四条数据加载成功');
                        reject('data4 err')
                    },500)
                })
            }
            
            let p = Promise.all( [getData1(), getData2(), getData3(), getData4() ])
            
            p.then(arr => {
                console.log(arr)  
            }, e => {
                console.log(e)  //data4 err
            })
            

当数组项为空时,直接决议成功:

function getData1(){
                return new Promise( (resolve,reject) => {
                    setTimeout( () => {
                        console.log('第一条数据加载成功');
                        resolve('data1');
                    },1000)
                })
            }
            
            function getData2(){
                return new Promise( (resolve,reject) =>{
                    setTimeout( () => {
                        console.log('第二条数据加载成功');
                        resolve('data2');
                    },1000)
                })
            }
            
            function getData3(){
                return new Promise( (resolve,reject) => {
                    setTimeout( () => {
                        console.log('第三条数据加载成功');
                        resolve('data3');
                    },1000)
                })
            }
            
            function getData4(){
                return new Promise( (resolve,reject) => {
                    setTimeout( () => {
                        console.log('第四条数据加载成功');
                        reject('data4 err')
                    },500)
                })
            }
            
            let p = Promise.all( [ ])
            
            p.then(()=> {
                console.log('数组为空')  // 数组为空
            }, e => {
                console.log(e)
            })

13. 手写Promise

实现步骤:

1、创建MyPromise类
2、通过构造函数constructor,在执行这个类的时候需要传递一个执行器进去并立即调用
3、定义resolve和reject(定义为箭头函数:避免直接调用时this指向全局window问题)
4、定义状态常量(成功fulfilled 失败rejected 等待pending),初始化为pending。
5、完成resolve和reject函数的状态改变(注意:需判断当前状态是否可以改变)
6、MyPromise类中定义value和reason,用来储存执行器执行成功和失败的返回值
7、MyPromise类中添加then方法,成功回调有一个参数 表示成功之后的值;失败回调有一个参数 8、表示失败后的原因
9、处理异步逻辑(pending状态下在then中将回调存起来)
10、实现then方法多次调用添加多个处理函数
11、实现then方法链式调用(写一个函数方法专门判断回调的结果是普通值还是promise,then方12、法返回的仍然是一个promise)
13、处理promise返回值各种类型情况(普通值,promise)
14、then方法链式调用识别Promise对象自返回
15、Promise实现捕获错误及then链式调用其他状态代码补充
16、将then方法的参数变为可选参数
17、Promise.all
18、Promise.resolve 返回一个promise
19、 finally方法 不管成功失败都会执行一次
20、catch方法的实现

// 4. 定义状态常量(成功fulfilled 失败rejected 等待pending),初始化为pending。
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
 
// 1 创建MyPromise类 
class MyPromise {
  // 2 通过构造函数constructor,在执行这个类的时候需要传递一个执行器进去并立即调用
  constructor(executor) {
    // 13 Promise实现捕获错误
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }
  status = PENDING
  //  6. MyPromise类中定义value和reason,用来储存执行器执行成功和失败的返回值
  value = null
  reason = null
  // 9. 实现then方法多次调用添加多个处理函数 初始化回调为数组依次执行
  successCallback = []
  failCallback = []
  // 3. 定义resolve和reject(定义为箭头函数:避免直接调用时this指向全局window问题)
  resolve = value => {
    // 5. 完成resolve函数的状态改变(注意:需判断当前状态是否可以改变)
    // 判断当前状态是否可改变
    if(this.status !== PENDING) return
    // 改变当前状态
    this.status = FULFILLED
    // 保存返回值
    this.value = value
    // 执行成功回调
    while(this.successCallback.length) {
      this.successCallback.shift()(this.value)
    }
  }
  reject = reason => {
    // 5. 完成reject函数的状态改变(注意:需判断当前状态是否可以改变)
    // 判断当前状态是否可改变
    if(this.status !== PENDING) return
    // 改变当前状态
    this.status = REJECTED
    // 保存返回值
    this.reason = reason
    // 执行失败回调
    while(this.failCallback.length) {
      this.failCallback.shift()(this.reason)
    }
  }
  // 7. MyPromise类中添加then方法,成功回调有一个参数 表示成功之后的值;失败回调有一个参数 表示失败后的原因
  then(successCallback, failCallback) {
    // 14 将then方法的参数变为可选参数
    successCallback = successCallback ? successCallback : value => this.value
    failCallback = failCallback ? failCallback : reason => {throw this.reason}
    // 10. 实现then方法链式调用(写一个函数方法专门判断回调的结果是普通值还是promise,then方法返回的仍然是一个promise)
    let promise2 = new MyPromise((resolve, reject) => {
        // 判断当前状态 执行对应回调 异步情况下存储当前回调等待执行
        if(this.status === FULFILLED) {
           // 异步
           setTimeout(() => {
            // 13 then方法捕获错误
            try {
              // 异步获取到promise2
              let x = successCallback(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        } else if(this.status === REJECTED) {
          // 异步
          setTimeout(() => {
            // 13 then方法捕获错误
            try {
              // 异步获取到promise2
              let x = failCallback(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        } else {
          // 8. 处理异步逻辑(pending状态下在then中将回调存起来)
          this.successCallback.push(() => {
            try {
              let x = successCallback(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          })
          this.failCallback.push(() => {
            try {
              let x = failCallback(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          })
        }
    })
    return promise2
  }
  // 17. finally方法 不管成功失败都会执行一次
  finally(callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.reject(callback()).then(() => { throw reason })
    })
  }
  // 18. catch
  catch(failCallback) {
    return this.then(undefined, failCallback)
  }
  // 15. Promise.all
  static all (array) {
    let result = []
    let index
    return new Promise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value
        index++
        if(index === array.length) {
          resolve(result)
        }
      }
      for(let i = 0; i < array.length; i++) {
        let current = array[i]
        if(current instanceof MyPromise) {
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          addData(i, array[i])
        }
      }
    })
  }
  // 16. Promise.resolve 返回一个promise
  static resolve(value) {
    if(value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
  }
}
 
// 处理promise返回值各种类型情况(普通值,promise)
function resolvePromise(promise2, x, resolve, reject) {
  if(promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if(x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

14. async/await原理

async/await 是一种异步编程的语法糖,它可以让我们用同步的方式写出异步的代码,提高代码的可读性和可维护性。它的原理是基于 Promise 和 generator 函数的。

async 是一个关键字,用来声明一个函数是异步的。它会返回一个 Promise 对象,表示函数的执行结果。await 是一个运算符,用来等待一个 Promise 对象或者任意表达式的值。它只能出现在 async 函数中,否则会报错。

generator 函数是一种特殊的函数,它配合yield关键字可以暂停执行和恢复执行,返回一个迭代器对象。每次调用迭代器的 next 方法,就会执行 generator 函数内部的一段代码,直到遇到 yield 关键字,暂停并返回 yield 后面的表达式的值。再次调用 next 方法时,会从上次暂停的地方继续执行,直到遇到 return 关键字或者函数结束,返回 {value: undefined, done: true} 表示迭代完成。

async/await 的实现原理就是利用了 generator 函数和 Promise 对象。async 函数在执行时,会被编译器转换成一个 generator 函数,并自动执行。当遇到 await 表达式时,会暂停执行,并将 await 后面的表达式包装成一个 Promise 对象,当这个 Promise 对象被 resolve 时,会再次调用 next 方法,并将 resolve 的值作为next方法的参数,也就是作为上一次 await 表达式的返回值。依次执行,遇到await就会重复上述过程,知道函数结束。这样就实现了异步操作的同步化表达。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值