这是我参与更文挑战的第7天,活动详情查看: 更文挑战
为加强巩固对Promise的理解,学习封装一个属于自己的Promise类库,
话接上文,如果大家对Promise的基础知识还是不太清楚的,可以先看看这篇Promise重学-基础知识篇
1, Promise类库封装
class Promise {
// 构造方法
constructor(executor) {
// 声明构造函数
// 添加属性
this.PromiseState = 'pending'
this.PromiseResult = null
// 声明属性
this.callbacks = [];
// 保存实例对象的this的值
const self = this;
function resolve(data) {
// 判断状态,状态只能修改一次
if (self.PromiseState !== 'pending') return ;
// 修改对象状态 PromiseState
self.PromiseState = 'fulfilled';
// 设置对象结果值 PromiseResult
self.PromiseResult = data;
// 调用成功的回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(data)
})
})
}
function reject(data) {
// 判断状态,状态只能修改一次
if (self.PromiseState !== 'pending') return ;
// 修改对象状态 PromiseState
self.PromiseState = 'rejected'
// 设置对象结果值 PromiseResult
self.PromiseResult = data
// 调用失败的回调函数
setTimeout(()=> {
self.callbacks.forEach(item => {
item.onRejected(data)
})
})
}
try {
// 同步调用
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
// then 方法封装
then(onResolved, onRejected) {
const self = this;
// 判断回调函数参数
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason;
}
}
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new Promise((resolve, reject) => {
// 封装函数
function callback(type) {
try {
// 获取回调函数的执行结果
let result = type(self.PromiseResult)
// 判断
if(result instanceof Promise) {
// 如果是promise类型对象
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
// 结果的对象状态改为成功
resolve(result)
}
} catch(e) {
reject(e)
}
}
// 调用回调函数
if(this.PromiseState === 'fulfilled') {
setTimeout(()=> {
callback(onResolved)
})
}
if(this.PromiseState === 'rejected') {
setTimeout(()=> {
callback(onRejected)
})
}
if(this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function() {
callback(onResolved)
},
onRejected: function() {
callback(onRejected)
}
})
}
})
}
// catch 方法封装
catch(onRejected) {
return this.then(undefined, onRejected)
}
// resolve方法
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v)
}, r=> {
reject(r)
})
} else {
resolve(value)
}
})
}
// reject 方法封装
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
// all 方法封装
static all(promise) {
let count = 0;
let arr = [] // 保存成功的结果
return new Promise((resolve, reject) => {
for(let i=0; i<promise.length; i++) {
promise[i].then(v=>{
// 每个promise对象都成功
count++;
arr[i] = v;
if(count === promise.length) {
resolve(arr)
}
}, r=>{
reject(r)
})
}
})
}
// race 方法封装
static race(promise) {
return new Promise((resolve,reject) => {
for(let i=0;i<promise.length; i++) {
promise[i].then(v=> {
resolve(v)
},r=> {
reject(r)
})
}
})
}
}
测试代码
let p = new Promise((resolve, reject) => {
setTimeout(()=> {
reject('ok')
}, 100)
})
const res = p.then(value => {
console.log(value)
}, reason=> {
console.warn(reason)
})
console.log(res)
const res2 = p.catch(reason => {
console.log(reason)
})
console.log(res2)
let p4 = new Promise((resolve, reject) => {
resolve('ok')
console.log(111)
})
p4.then(value => {
console.log(222)
})
console.log(333)
注意: 回调函数里面的代码是异步执行的,
大家可以看看下面的代码执行结果:
let p = new Promise((resolve, reject) => {
resolve('ok')
console.log(111)
})
p.then(value => {
console.log(222)
})
console.log(333)
答案是: 111 —> 333 —> 222
同步代码先执行,异步代码需要等到同步代码执行完之后才开始执行
2, async 函数
1,函数的返回值为promise对象
2, promise对象的结果由async函数执行的返回值决定
3, await 表达式
1, await右侧的表达式一般为promise对象,但也可以是其他的值
2,如果表达式是promise对象,await返回的是promise成功的值
3,如果表达式是其他的值,直接将此值作为await的返回值
注意: await 必须写在async函数中, 但是async函数中可以没有await
注意:如果await的promise失败了,就会抛出异常,需要通过try…catch捕获
async function main() {
let p = new Promise((resolve, reject) => {
resolve('ok')
})
// 1 右侧为promise的情况
let res = await p;
// 2, 右侧为其他类型的数据
let res2 = await 20;
// 如果promises是失败的状态, 上面代码把reject('error')注释掉了,可以切换过来看效果
try {
let res3 = await p
} catch(e) {
console.log(e)
}
}
main()
4, async 和 await 结合
/**
* 读取多个文件内容,并拼接
*/
const fs = require('fs')
const util = require('util')
const mineReadFile = util.promisify(fs.readFile)
async function myReadFile() {
// 读取第一个文件
try {
let data1 = await mineReadFile('./package/content1.txt')
let data2 = await mineReadFile('./package/content2.txt')
let data3 = await mineReadFile('./package/content3.txt')
console.log(data1 + data2 + data3)
}catch(e) {
console.log(e)
}
}
mineReadFile()
/**
* 封装AJAX接口请求
* @param {*} url
*/
function sendAjax(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url);
xhr.send()
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
if(xhr.status >=200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
}
xxxx.addEventListener('click', async function() {
let content = await sendAjax('https://api.apiopen.top/getJoke')
console.log(content)
})
5, 说说AJAX,AXIOS, FETCH的区别
1,AJAX:异步网络请求,它实现了页面可以无刷新的请求数据。以往,页面表单提交数据,在用户点击完”submit“按钮后,页面会强制刷新一下,体验十分不友好。
var request = new XMLHttpRequest(); // 创建XMLHttpRequest对象
//ajax是异步的,设置回调函数
request.onreadystatechange = function () { // 状态发生变化时,函数被回调
if (request.readyState === 4) { // 成功完成
// 判断响应状态码
if (request.status === 200) {
// 成功,通过responseText拿到响应的文本:
return success(request.responseText);
} else {
// 失败,根据响应码判断失败原因:
return fail(request.status);
}
} else {
// HTTP请求还在继续...
}
}
// 发送请求:
request.open('GET', '/api/categories');
request.setRequestHeader("Content-Type", "application/json") //设置请求头
request.send();//到这一步,请求才正式发出
2,AXIOS:axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
因为axios设计简洁,API简单,支持浏览器和node,
3,FETCH:fetch是前端发展的一种新技术产物
- 当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
- 默认情况下,fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项)。
fetch代表着更先进的技术方向,但是目前兼容性不是很好,在项目中使用的时候得慎重。