【ES6】掌握Promise和利用Promise封装ajax

news/2024/7/11 0:40:10 标签: es6, ajax, javascript, 前端

💻 【ES6】掌握Promise和利用Promise封装ajax 🏠专栏:JavaScript
👀个人主页:繁星学编程🍁
🧑个人简介:一个不断提高自我的平凡人🚀
🔊分享方向:目前主攻前端,其他知识也会阶段性分享🍀
👊格言:☀️没有走不通的路,只有不敢走的人!☀️
👉让我们一起进步,一起成为更好的自己!!!🎁

文章目录

  • 【ES6】掌握Promise和利用Promise封装ajax
    • 一. 概念
    • 二. Promise的静态方法
      • (1) all
      • (2) race
      • (3) allSettled (ES2020新增)
      • (4) any
      • (5) Promise的其他方法
    • 三. JavaScript容错处理
      • try...catch...
    • 四. 利用promise封装ajax
    • 五. async和await

ajax_8">【ES6】掌握Promise和利用Promise封装ajax

一. 概念

Promise是ES6 新增的特性

目的:用来解决异步解决回调地狱问题

利用Promise类创建promise实例对象,类的参数是回调函数,回调函数又有两个参数(一般约定为resolve(成功)和reject(失败))

Promise 的基本语法

const 实例对象 = new Promise(回调函数)

eg:

const p = new Promise(function(resolve,reject){});

回调函数中的参数:

  • resolve:成功的回调
  • reject:失败的回调

Promise 的三个状态:

  • 持续:pending
  • 成功:fulfilled
  • 失败:rejected

Promise 的两种转换(三种状态变成两种可能):

  • 从持续状态转换到成功
  • 从持续状态转换到失败

Promise 的实例原型方法:

  • then(函数):成功状态的时候执行的方法
  • catch(函数):失败状态的时候执行的方法
  • finally(函数) :结束时执行的方法,不管状态是成功还是失败,finally都会执行

Promise 的调用方式 (链式调用)

  • 当在第一个then里面返回 一个新的promise对象的时候
  • 可以在第一个then后面继续第二个then
const p = new Promise(function (resolve, reject) {
    const time = Math.random() * 2000 + 2000
    console.log(time);
    setTimeout(() => {
        if (time > 3000) {
            console.log('成功');
            resolve("success");
        } else {
            console.log('失败');
            reject("reject");
        }
    }, 2000);
});
// 成功状态的时候执行
p.then(function (sa) {
    console.log(sa);
})
// 失败状态的时候执行
p.catch(function (sc) {
    console.log(sc);
});

使用函数简单封装Promise代码

function fz() {
    const p = new Promise(function (resolve, reject) {
        const time = Math.random() * 2000 + 2000
        console.log(time);
        setTimeout(() => {
            if (time > 3000) {
                resolve("success")
            } else {
                reject("reject");
            }
        }, 2000);
    });
    return p;
}
fz().then(function (num) {
    console.log("1成功", num);
}).then(function (num) {
    console.log("2成功", num);
}).then(function (num) {
    console.log("3成功", num);
}).catch(function (num) {
    console.log('失败', num);
}).finally(function () {
    console.log('无论最终是成功还是失败,都会执行');
})

二. Promise的静态方法

(1) all

语法:Promise.all([多个 Promise])

作用:用于将多个 Promise 实例,包装成一个新的 Promise 实例,接受一个数组作为参数,只有数组里面的每个状态都变成resolve,则新的 Promise 实例状态才会变成resolve。

function fz1() {
    const p = new Promise(function (resolve, reject) {
        const time = Math.random() * 2000 + 2000;
        console.log(time, '第一个请求');
        setTimeout(() => {
            if (time > 3000) {
                resolve({ code: 1, message: '成功' })
            } else {
                if (time < 2500) {
                    resolve({ code: 0, message: '失败1' })
                } else {
                    resolve({ code: 2, message: '失败2' })
                }
            }
        }, time)
    })
    return p;
}
function fz2() {
    const p = new Promise(function (resolve, reject) {
        const time = Math.random() * 2000 + 2000;
        console.log(time, '第二个请求');
        setTimeout(() => {
            if (time > 3000) {
                resolve({ code: 1, message: '成功' })
            } else {
                if (time < 2500) {
                    resolve({ code: 0, message: '失败1' })
                } else {
                    resolve({ code: 2, message: '失败2' })
                }
            }
        }, time)
    })
    return p;
}
function fz3() {
    const p = new Promise(function (resolve, reject) {
        const time = Math.random() * 2000 + 2000;
        console.log(time, '第三个请求');
        setTimeout(() => {
            if (time > 3000) {
                resolve({ code: 1, message: '成功' })
            } else {
                if (time < 2500) {
                    resolve({ code: 0, message: '失败1' })
                } else {
                    resolve({ code: 2, message: '失败2' })
                }
            }
        }, time)
    })
    return p;
}

Promise
    .all([fz1(), fz2(), fz3()])
    .then(res => console.log('成功', res))
    .catch(err => console.log('失败', err))

(2) race

语法:Promise.race([多个promise])

作用:将多个 Promise 实例,包装成一个新的 Promise 实例,接受一个数组作为参数,只要其中有一个实例率先改变状态,则整个的状态就跟着改变。

function fz1() {
    const p = new Promise(function (resolve, reject) {
        const time = Math.random() * 2000 + 2000;
        console.log(time, '第一个请求');
        setTimeout(() => {
            if (time > 3000) {
                resolve({ code: 1, message: '成功' })
            } else {
                if (time < 2500) {
                    resolve({ code: 0, message: '失败1' })
                } else {
                    resolve({ code: 2, message: '失败2' })
                }
            }
        }, time)
    })
    return p;
}
function fz2() {
    const p = new Promise(function (resolve, reject) {
        const time = Math.random() * 2000 + 2000;
        console.log(time, '第二个请求');
        setTimeout(() => {
            if (time > 3000) {
                resolve({ code: 1, message: '成功' })
            } else {
                if (time < 2500) {
                    resolve({ code: 0, message: '失败1' })
                } else {
                    resolve({ code: 2, message: '失败2' })
                }
            }
        }, time)
    })
    return p;
}
function fz3() {
    const p = new Promise(function (resolve, reject) {
        const time = Math.random() * 2000 + 2000;
        console.log(time, '第三个请求');
        setTimeout(() => {
            if (time > 3000) {
                resolve({ code: 1, message: '成功' })
            } else {
                if (time < 2500) {
                    resolve({ code: 0, message: '失败1' })
                } else {
                    resolve({ code: 2, message: '失败2' })
                }
            }
        }, time)
    })
    return p;
}
Promise
    .race([fz1(), fz2(), fz3()])
    .then(res => console.log('成功', res))
    .catch(err => console.log('失败', err))

(3) allSettled (ES2020新增)

语法:Promise.allSettle([多个promise])

作用:方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。

简单理解就是:不管是成功还是失败,都会触发,并会把结果用数组的形式返回,数组里面放着每一个promise的结果

let p1 = new Promise((resolve, reject) => {
    resolve('1')
});
let p2 = new Promise((resolve, reject) => {
    reject('2')
});
let p3 = new Promise((resolve, reject) => {
    reject('3')
});

let allSettled = Promise.allSettled([p1, p2, p3]);
allSettled.then((res) => {
    console.log('then:', res); // 返回数组,数组里面有三个对象,对象里面区分成功和失败的值,状态是正常的。
    //status:resolve  value  /  status:reject reason
    console.log(res[0].status); // fulfilled
    console.log(res[0].value); // 1
    console.log(res[1].status); // rejected
    console.log(res[1].reason); // 2
});

(4) any

语法:Promise.any([多个promise])

作用:该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

let p1 = new Promise((resolve, reject) => {
    reject('1')
    // resolve('1')
});
let p2 = new Promise((resolve, reject) => {
    // reject('2')
    resolve('2')
});
let p3 = new Promise((resolve, reject) => {
    reject('3')
});

// 解析:
// 如果遇到成功的请求,通过then返回resolve传递给then对应的值,如果都是失败的状态,最终走catch输出默认的字符( All promises were rejected),执行catch里面的代码,无法获取reject里面传入的值。
let any = Promise.any([p1, p2, p3]);
any.then(res => {
    console.log('then:', res);
}).catch(msg => {
    console.log('catch代码'); 
    console.log(msg); //默认的字符All promises were rejected
});

(5) Promise的其他方法

以下两个方法主要是开发人员调试用的

  • resolve()强行把promise的状态改为成功

    let num = 10;
    Promise.resolve(num).then(res => console.log(res))
    console.log(1);
    
  • reject()强行把Promise的状态改为失败

    let num = 10;
    Promise.reject(num).then().catch(res => {
        console.log(res) // 10
    });
    console.log(1); // 1
    

三. JavaScript容错处理

因为js代码如果出现错误,立刻停止,后续的代码不会执行。

console.log(a); // referenceError:a is not defined  
console.log(1);

所以需要采用方法使js代码出现错误时,不会影响到后续代码

try…catch…

语法1

try {执行代码} catch (err) {执行代码}

使用

首先执行try里面的代码,如果不报错,catch不执行
如果报错,不会抛出错误,而是执行catch,把错误信息给到err。

try {
    console.log(a);
} catch (e) { //e:try里面如果存在错误,e就是try里面的错误信息
    console.log(e); // referenceError:a is not defined  
    console.log('这里是catch的输出'); // 这里是catch的输出
}

console.log('后面后续的代码输出'); // 后面后续的代码输出
console.log('后面后续的代码输出'); // 后面后续的代码输出

语法2

try {执行代码} catch (err) {执行代码} finally{执行代码}

:finally里面的语法不管是执行try还是catch,finally里面的代码一定会执行。

try {
    console.log('a');
} catch (e) { //e:try里面如果存在错误,e就是try里面的错误信息
    console.log(e); // referenceError:a is not defined
    console.log('这里是catch的输出');
} finally { //收尾的工作,不再使用的对象,变量,设置null
    console.log('finally一定会执行');
}
/*
输出结果:
	a
	finally一定会执行
*/

应用场景-案例

随机生成一个数字,添加判断条件,最终输出成功或失败结果

function fz() {
    const p = new Promise(function (resolve, reject) {
        const time = Math.random() * 2000 + 2000
        console.log(time);
        setTimeout(() => {
            if (time > 2500) {
                resolve("success")
            } else {
                reject("reject");
            }
        }, 2000);
    });
    return p;
}
async function cs() {
    try {
        const res1 = await fz();
        console.log(res1);
    } catch (err) {
        const res2 = await fz();
        console.log(res2);
    }
}
cs()

ajax_421">四. 利用promise封装ajax

封装前的分析:

/*
        封装ajax 
            function ajax(options){}
        调用封装的ajax
            ajax({})

        分析
            封装方案
                1. 回调函数方式
                    - 将来使用的时候,可能会出现回调地狱
                2. promise 
                    - 后面 then/catch
                    - 也可以用async/await
            参数
                1. 请求地址 url 必填
                2. 请求方式 method,选填(只允许 get post),要么不传,默认值 get
                3. 是否异步 async,选填  默认true
                4. 参数 data 选填,默认是'', (允许传查询字符串 和 对象)
                5. 请求头headers 选填,默认{content-type:'application/x-www-form-urlencoded'}
                6. 是否解析响应的参数 dataType, 选填 默认是string,  选填 json
*/

封装代码:

// 对象格式转查询字符串格式
function queryString(obj) {
    let str = ''
    for (let k in obj) {
        str += `&${k}=${obj[k]}`
    }
    return str.slice(1)
}

// 利用闭包保存url基准地址
function createAjax(url) {
    // 设置一个变量作为基准地址,并长期保存这个变量
    let baseUrl = url

    // 封装ajax函数
    function ajax(options = {}) {
        // 1.验证参数格式
        // 1-1.验证url格式:必填
        if (!options.url) {
            // 没填抛出错误:'url必填'
            throw new Error('url必填')
        }
        // 1-2.验证method:要么不写,要么get或post
        if (!(options.method === undefined || /^(get|post)$/i.test(options.method))) {
            throw new Error('请求方式支持get 和 post')
        }
        // 1-3.验证async:要么不写,要么true/false ===> 参数是布尔类型
        if (!(options.async === undefined || typeof options.async === 'boolean')) {
            throw new Error('async需要一个布尔值')
        }
        // 1-4.验证data:要么不写,支持字符串格式/对象格式
        if (!(options.data === undefined || typeof options.data === 'string' || Object.prototype.toString.call(options.data) === '[object Object]')) {
            throw new Error('data需要一个字符串格式或者对象格式')
        }
        // 1-5.验证headers:要么不写,要么是对象格式
        if (!(options.headers === undefined || Object.prototype.toString.call(options.headers) === '[object Object]')) {
            throw new Error('headers需要一个对象格式')
        }
        // 1-6.验证dataType参数:要么不写,要么是string 或者 json 
        if (!(options.dataType === undefined || /^(string|json)$/.test(options.dataType))) {
            throw new Error('dataType 只能写 string json')
        }

        // 2.准备一个默认值
        const _default = {
            // 修改请求地址:基准地址+options.url
            // eg:options.url = /test/first
            url: baseUrl + options.url,
            // method只有三个选择(没写,get,post)
            method: options.method || 'get',
            // async只有三个选择(没写,true,false)并且没写:默认值是true
            // 一个ES2020新的操作符 ??
            // ?? 空值运算符: 只有左边的是undefined或者null, 才使用右边的
            async: options.async ?? true,
            // data只能有三个选择:字符串、对象、undefined,默认值是''
            data: options.data || '',
            // headers只能是 undefined或者对象,默认是{'content-type': 'application/x-www-form-urlencoded'}
            headers: { 'content-type': 'application/x-www-form-urlencoded', ...options.headers },
            // 是否解析响应的参数 dataType, (选填) 默认是string,  (选填) json
            dataType: options.dataType || 'string'
        }
        // 2-1.data可能是对象,如果是对象,需要转为查询字符串格式
        if (typeof _default.data === 'object') {
            _default.data = queryString(_default.data)
        }
        // 2-2.请求方式是get并且data有值,url需要拼接上刚刚转换的data
        if (/^get$/i.test(_default.method) && _default.data) {
            _default.url = _default.url + '?' + _default.data
        }

        // 3 创建ajax对象 按照promise方式
        const p = new Promise(function (resolve, reject) {
            const xhr = new XMLHttpRequest()
            xhr.open(_default.method, _default.url, false)
            xhr.onload = function () {
                // 这里的xhr.readyState必然是4,onload是一个请求完成的事件
                // 如果请求成功,xhr状态码是4,http状态码是200-299 成功
                // 需求, 根据业务进一步封装,如果后端返回的内容中,code为1,成功,其他失败
                if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
                    let res = xhr.responseText
                    // 根据服务端返回的内容判断
                    // 首先代码进入到这里,http请求成功,可以成功从服务端拿到响应内容
                    // 响应内容分两种情况,
                    // 一个是正确的
                    // 一个是业务错误 (参数不正确,代码错误,其他....)
                    // 根据后端返回的内容判断, code:1 正确的 成功
                    // code 不是1 错误(用户密码错误,登录时间过期 )
                    try {
                        res = JSON.parse(res)
                    }
                    catch (err) {
                        return
                    }
                    if (res.code === 1) {
                        // 正确的:成功返回正确的数据 
                        resolve({ code: 1, message: "成功", data: _default.dataType === 'string' ? xhr.responseText : res })
                    } else {
                        // 错误:包括各种错误 
                        resolve({ code: 0, message: '业务失败', err: res })
                    }
                } else {
                    // 请求不成功 -- 进入这里的代码必然是http状态码不是200-299的情况
                    let res = xhr.responseText
                    resolve({ code: 0, message: '网络请求失败', err: res })
                }
            }
            // 如果post 
            if (/^post$/i.test(_default.method)) {
                xhr.setRequestHeader('content-type', _default.headers['content-type'])
            }
            // 如果有token 
            if (_default.headers.authorization) {
                xhr.setRequestHeader('authorization', _default.headers['authorization'])
            }
            // 如果是post 需要发送data
            if (/^post$/i.test(_default.method)) {
                xhr.send(_default.data)
            } else {
                xhr.send()
            }
        })
        // 把promise对象返回
        return p
    }
    // 返回ajax函数
    return ajax
}
// 设置url基准地址
let ajax = createAjax('http://localhost:8888')

测试:

// 1.利用Promise的then()方法测试封装的ajax
// ajax({
//     url: '/test/first',
// }).then(res => {
//     console.log(res);
// })

// 2.利用async await 测试封装的ajax
async function fn() {
    const res = await ajax({ url: '/goods/list', dataType: 'json' })
    console.log(res);
}
fn()

:测试前需要开启本地服务器

本地服务器下载地址:Ajax案例测试本地服务器-Node.js文档类资源-CSDN文库

五. async和await

  1. async其实就是promise的语法糖。函数前面加一个async,函数里面异步操作的方法前加一个await关键字。

    顾名思义,await就是让你等一下,执行完了再继续往下走。

    注意:await只能在async函数中执行,否则会报错。

    目的:让异步代码像同步代码一样执行。

  2. async:异步的意思,作用是申明一个异步函数,函数的返回值是promise 对象

    async function fn1() { }
    console.log(fn1()); //async函数隐式返回一个promise对象
    
  3. await:等待

    await是async+wait的结合 即异步等待,async和await 二者必须是结合着使用。

    :匿名函数也可以使用async和await

    !async function () {
        let h = await new Promise((resolve, reject) => {
            resolve('hello')
        });
        console.log(h); //hello  证明await就是promise里面then的语法糖。
    }();
    

结束语

希望对您有一点点帮助,如有错误欢迎小伙伴指正。
👍点赞:您的赞赏是我前进的动力!
⭐收藏:您的支持我是创作的源泉!
✍评论:您的建议是我改进的良药!
一起加油!!!💪💪💪


http://www.niftyadmin.cn/n/56314.html

相关文章

我是如何转岗成为数据分析师?

Datawhale干货 作者&#xff1a;孟禹&#xff0c;数据分析师&#xff0c;Datawhale邀约作者笔者背景介绍&#xff1a;工作5年半&#xff0c;前4年在K12在线教育负责教研和用户转化&#xff0c;21年双减之后跳槽到一家新消费品牌公司做数据分析师&#xff0c;跨了行转了岗。现在…

2022年12月电子学会Python等级考试试卷(三级)答案解析

目录 一、单选题(共25题&#xff0c;共50分) 二、判断题(共10题&#xff0c;共20分) 三、编程题(共3题&#xff0c;共30分) 青少年软件编程&#xff08;Python&#xff09;等级考试试卷&#xff08;三级&#xff09; 一、单选题(共25题&#xff0c;共50分) 1. 列表L1中全是…

注册ChatGPT的辛酸血泪史,不能算教程的教程

注册ChatGPT的血泪史 2月份了&#xff0c;改论文降重了&#xff0c;所以想搞个ChatGPT玩玩&#xff0c;本以为有渠道能顺序上车&#xff0c;但是看了很多教程&#xff0c;也进了很多交流群&#xff0c;都喵的(要不是卖号&#xff0c;租体验&#xff0c;liar)&#xff0c;所以自…

FoveaBox原理与代码解析

paper&#xff1a;FoveaBox: Beyond Anchor-based Object Detectorcode&#xff1a;https://github.com/taokong/FoveaBox背景基于anchor的检测模型需要仔细设计anchor&#xff0c;常用方法之一是根据特定数据集的统计结果确定anchor的number、scale、ratio等&#xff0c;但这种…

Redis 持久化

持久化是指数据写到物理硬盘里&#xff0c;即便程序崩溃、或者电脑重启&#xff0c;依然能够恢复。Redis提供了两种持久化机制&#xff1a;RDB和AOF。 RDB(Redis Database): RDB文件相当于内存快照&#xff0c;保存了某个时间点数据库信息。使用RDB文件恢复很简单&#xff0c;将…

与六年测试工程师促膝长谈,他分享的这些让我对软件测试工作有了全新的认知~

不知不觉已经从事软件测试六年了&#xff0c;2016年毕业到进入外包公司外包给微软做软件测试&#xff0c; 到现在加入著名的外企。六年的时间过得真快。长期的测试工作也让我对软件测试有了比较深入的认识。但是我至今还是一个底层的测试人员&#xff0c;我的看法都比较狭隘&am…

IDE2022源码编译tomcat

因为学习需要&#xff0c;我需要源码编译运行tomcat对其源码进行一个简单的追踪分析。由于先前并未接触过java相关的知识&#xff0c;安装阻力巨大。最后请教我的开发朋友才解决了最后的问题。将其整理出来&#xff0c;让大家能够快速完成相关的部署。本文仅解决tomcat-8.5.46版…

(day12) 自学Java——集合进阶(双列集合)

目录 1.双列集合特点 Map遍历三种方式 2.HashMap 3.LinkedHashMap 4.TreeMap 5.源码解析 6.可变参数(形参个数可变) 7.Collections 8.综合练习 1.双列集合特点 ①双列集合一次需要存一对数据&#xff0c;分别为键和值 ②键不能重复&#xff0c;值可以重复 ③键和值是一…