Promise笔记

Javascript   2025-01-05 11:25   66   0  

Promise模式实现:

promise.all()
promise.allSettled()
promise.any()
promise.race()
promise.reject()
promise.resolve()


一、Promise小白怎么用?从一个故事开始吧

1、先来一段废话故事

您是一名在古老迷失城市中探险的冒险家。您身处一间装饰华丽的房间中,四周布满了古老的壁画和雕塑。您发现有两个通道分别通向不同的方向,分别是:一个黑暗的通道和一个明亮的通道。

黑暗的通道中充满了神秘的气息,您能感受到其中蕴含的古老力量。您并不知道它会带您去哪里,但您可以感受到其中蕴藏的危险和未知。另一方面,明亮的通道看起来温暖舒适,您可以看到远处照耀的阳光。您能听到一些欢快的音乐声,似乎在那个方向上有一些人在欢庆。

2、再来看一段神秘的小短片

https://drive.weixin.qq.com/s?k=AOoAUwfiAAwVgJbsSaAUEA_QbjAFk

3、这么久了还不进入正题

先来了解几个单词,几个Promise的术语

● Promise的三个状态:pending(等待状态)、fulfilled(已成功状态)和 rejected(已失败状态)

● Promise的三个常用回调函数(咱说的是常用):

    1. 实例化对象时传入的回调函数

    2. then方法的传入的两个回调函数

    3. catch方法传入的一个回调函数

以作业为例,继续深入了解Promise

1、开始写作业,此时Promise状态为pending,我们可以理解为初始状态,也可以理解为业务处理中

2、作业完成情况有两种,一种是写完了,一种是被狗吃了

3、无论哪种情况,必须要告诉老师,成功了就通过resolve通道暂存数据,同时会更改状态为成功fulfilled;失败了就通过reject通道暂存数据,同时修改状态为rejected。

4、老师如何获取resolve或reject通道传来的数据呢?可以使用Promise对象的.then方法获取resolve或reject的结果,或者使用.catch方法获取reject通道的信息。

5、简单来一段代码看看

const p = new Promise(function (resolve, reject) {
    resolve('通过成功通道存储数据')
})
//...如果不使用then,提取,则数据一直暂存在p对象中
//提取结果
p.then(function (val) {
    console.log(val)
})


6、这些写法都是固定的,建议使用promise之前一定要充分了解回调函数


7、咱们再升级一下,写作业用了5秒钟

const p1 = new Promise(function (resolve, reject) {
    //写作业用了5秒钟,5秒钟之后,把成功的作业暂存到resolve通道中
    setTimeout(() => {
        resolve('这是作业')
    },5000)
})
//上面代码中调用了resolve的时候,then里面的回调函数才会触发,这里有个时间差或者时空差的感觉
p1.then(function (val) {
    console.log(val)
})


8、再升级一下,这作业他也有被狗吃了的时候,我们假定5秒钟之外就被狗吃了,5秒钟之内就是完成的

const p2 = new Promise(function(resolve,reject) {
    //生成一个1~6秒之间的随机数
    const time = Math.random() * 4000 + 2000
    setTimeout(() => {
        if (time <= 5000) {
            resolve('成功交作业啦')
        }else{
            reject(`作业被狗吃了,耗费了${time}秒`)
        }
    },time)
})
//成功使用then来接,失败了怎么拿到参数呢?
//用then的第二个参数来拿失败的参数
p2.then(function(val){
    console.log(val)
},function(err){
    console.log('估计是被狗吃了', err)
})


9、除了then的第二个参数可以拿到失败的结果,还可以通过catch方法拿到结果,一会再讨论这两种用法的区别,先看catch的用法,注意这里需要连用

p2.then(function(val){
    console.log(val)
}).catch((reason) => {
    //输出失败原因,大概率是被狗吃了
    console.log('估计是被狗吃了', reason)
})


10、再看一种常用的连用的写法

new Promise(function (resolve, reject) {
//生成一个1~6秒之间的随机数
const time = Math.random() * 4000 + 2000
    setTimeout(()=>{
        if(time<=5000) {
            resolve('成功交作业啦')
        }else{
            reject(`作业被狗吃了,耗费了${time}秒`)
        }
    }, time)
}).then(function(val){
    console.log(val)
}).catch((reason)=>{
    //输出失败原因,大概率是被狗吃了
    console.log('估计是被狗吃了', reason)
})


11、一些需注意的地方

1. resolve和reject只是一个形参的名字,对应实际的值是promise内部的函数,调用这两个其实调用的就是promise内部的某个函数而已,这个名字可以随便去改,例如

new Promise(function(ok,fail) {
    //此时此刻形参叫ok,但实际代表的是promise内部函数
    ok('ojbk')
}).then((res)=>{
    //promise内部会存储数据,传给res这个变量,此时res 值就是ojbk
    console.log(res)
})

2. new Promise(构造器的参数是一个函数),这个函数会同步执行,代码执行到这里的时候就会立即执行


12、小结

1. Promise通过构造函数同步执行,执行结果调用promise的内部函数存储,通常叫resolve和reject,一个是成功时存储存储使用的通道,另一个是失败时存储的通道,无论存储到哪个通道中,都是写代码的人去定义的

2. then有两个参数,分别是两个函数类型的参数,第一个参数是在调用resolve时会触发,第二个参数是在调用reject时触发

3. catch方法可以替代then的第二个参数,拿到reject结果


13、附赠then第二个参数和catch的区别

在then的第一个参数里面的代码,如果出现异常的时候,不用手动的try...catch,通过promise实例对象的.catch可以捕获then内出现的异常,但注意,catch不会捕获构造函数代码中的错误,来看例子

new Promise(function(ok,fail) {
    setTimeout(()=>{
        //故意5秒后触发k的报错
        console.log(k)
    }, 5000)
}).then((res)=>{
    console.log(res)
}).catch(error=>{
    //这个时候,error是拿不到那个错误的,他不负责console.log(k)所在代码块中出现的错误
    console.log(error)
})

再看一个catch方法能捕获什么地方的错误


二、为什么要这么用,图个啥~

使用 Promise 的主要原因是他可以解决回调地狱(回调嵌套)问题,让代码变得更优雅,逻辑更清晰

举一个生活中的例子,早上起床第一件事是要穿拖鞋,第二件事是洗漱,第三件事是穿衣服,第四件事是查看“身手要钱”,第五件事是打开自家房门出去开车上班,每件事都需要串行,每一步与下一步都有关联关系

function foo() {
    //1、穿拖鞋开始
    setTimeout(() => {
        //1、2秒后穿拖鞋完成
        //2、洗漱开始
        setTimeout(() => {
            //2、2秒后洗漱完成
            //3、穿衣服开始
            setTimeout(()=>{
                //3、穿衣服完成
                //....不好意思看官,后边还有好几个步骤咱就意思一下,再写就吐了
            },2)
        }, 2)
    }, 2)
}

运行:foo()

就写这几层吧,是不是太恶心了

new Promise((resolve,reject)=>{
  //1、穿拖鞋
  setTimeout(()=>{
    resolve('穿拖鞋搞定')
  },2000)
}).then(val=>{
  //等待穿拖鞋完成后,会调用这个函数
  //2、洗漱
  //注意此处!!!,必须使用return 返回一个新的promise来完成链式调用
  const p = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('洗漱搞定')
        },2000)
    })
    return p
}).then(val => {
    //3、穿衣服,此处直接返回,没有使用中间变量
    return new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('穿衣服搞定')
    },2000)
    })
}).then(val => {
    //4、查看“身手要钱”
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('查看“身手要钱”搞定')
        },2000)
    })
}).then(val => {
    //5、开车去上班
    // 元气满满的一天
})


三、Promise其他方法

那么多方法,不讲那么多,race、all什么的网上一抓一大把

说说语法糖await和async的用法

先了解一个基础规则

● await必须修饰的是Promise对象

● await必须在async中使用

● await只能接收resolve通道的结果,reject结果会导致报错

● await修饰的代码在执行时,代码是卡住的,类似于alert,这句代码不执行完,后边的代码不会向下执行,这也类似于线程同步执行的概念,这也是await有用之处

● async修饰的必须是函数

● async修饰后的函数在执行之后会转为Promise对象

看一段简单的代码:

async function foo(){
    let k=await new Promise(function(resolve,reject) {
        setTimeout(() => {
        resolve('qfedu')
        }, 2000)
    })
    console.log(k)
}
foo()

这样用倒是更麻烦,我们把要处理的事黏黏糊糊多弄一些试一试

async function foo(){
    let level1=await new Promise((resolve, reject) => {
        //1、穿拖鞋
        setTimeout(()=>{
            resolve('穿拖鞋搞定')
        },1000)
    })
    //拿着第一步的结果,去第二步进行操作
    let level2 = await new Promise((resolve, reject) => {
        setTimeout(()=>{
            resolve(level1 + '洗漱搞定')
        },1000)
    })
    let level3 = await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(level2+'穿衣服搞定')
        },1000)
    })
    let level4 = await new Promise((resolve, reject) => {
        setTimeout(()=>{
            resolve(level3+'查看“身手要钱”搞定')
        },1000)
    })
    console.log(level4 + ',之后开车去上班,元气满满的一天')
}
foo()

输出结果:

这样代码看起来更加简洁,当然要重点考虑的问题是在整个从上到下的调用过程中,任何一个环节出现问题,都会影响下面的代码

再来,我们把代码聚焦到foo()方法调用之前和调用之后

console.log(1)

foo() //这个会输出 穿拖鞋搞定洗漱搞定穿衣服搞定查看“身手要钱”搞定......等

console.log(2)

思考一下,程序输出的顺序

注意,使用async包裹的代码,属于异步代码,会在同步代码之后执行

我们给按钮添加一个点击事件,看点击按钮如何让程序使用await顺序执行

async function foo() {
    let level1 = await new Promise((resolve, reject) => {
    //1、穿拖鞋
    setTimeout(() => {
        resolve('穿拖鞋搞定')
    }, 1000)
})

//拿着第一步的结果,去第二步进行操作
let level2 = await new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(level1 + '洗漱搞定')
    }, 1000)
})

let level3 = await new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(level2 + '穿衣服搞定')
    }, 1000)
})

let level4 = await new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(level3 + '查看“身手要钱”搞定')
    }, 1000)
})
    console.log(level4 + ',之后开车去qfedu上班,元气满满的一天')
}
window.onclick = foo;
//或者是
window.onclick = async function(){
//todo ...
//await new Promise...
//await new Promise...
//await new Promise...
//...
}

实际场景中,await 与async通常用来处理ajax请求类代码,让代码更简洁,再次强调,await只接收resolve结果,注意reject和error的错误要使用try...catch...处理异常。


【示例(亲测)】

<!DOCTYPE html>
<html>
<head>
<title>Layui</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!--<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>-->
<!--jquery不要用跨域CDN-->
<script src="/static/plugs/jquery-3.4.1/jquery-3.4.1.min.js"></script>
<!--<script src="https://cdn.jsdelivr.net/npm/es6-promise@4.2.8/dist/es6-promise.auto.min.js"></script>-->
<script>
//按钮1
function axiosmethod(){
axios.get("https://www.xxx.cn/index/index/abc")
.then(function(data){
    console.log('axios1方法:')
    console.log(data)
})
}

//按钮2
function promisemethod1() {
    var flag=true;
    var ceshi = new Promise(function(resolve, reject){
        if(flag === true) {
            resolve('promise状态为成功!')
        };
        if(flag === false) {
            reject('promise状态失败!')
        };
    })
	console.log('Promise2方法:')
	console.log(ceshi);
}

//按钮2
function promisemethod2(){
	var ceshi2 = new Promise(function(resolve, reject){
		$.ajax({
			url:"https://www.xxx.cn/index/index/abc/",
			type: 'get',
			//dataType: 'html',
			//contentType:"application/x-www-form-urlencoded;charset=utf-8",
			async: false,
			data: {
			    userid: 1
			},
			success: function(data) {
			    console.log(data)
			},
			error: function(xhr,textstatus,thrown) {
			    reject(error);
			}
		})
   })
   console.log('Promise3方法:')
   console.log(ceshi2)
}

//按钮4
function promisemethod3(url){
	var data = {'userid':1};
	return new Promise((resolve,reject) => {
		axios.post(url,data)
		.then((response) => {
		if (response.data) {
			resolve(JSON.parse(response.data).success);
			console.log(JSON.parse(response.data).success)
		} else {
			//alert(response.data);
			console.log(response)
			reject()
		}
	})
	.catch((error) => {
		alert('网络故障' + error);
		reject()
	});
})
}

//根据时间判断执行条件语句resolve和reject
function promisemethod4(){
	new Promise(function (resolve, reject) {
		//生成一个1~6秒之间的随机数
		const time = Math.random() * 4000 + 2000
		setTimeout(() => {
		if (time <= 5000) {
			resolve('成功交作业啦')
		} else {
			reject(`作业被狗吃了,耗费了${time}秒`)
		}
		}, time)
		}).then(function (val) {
		    console.log(val)
		}).catch((reason) => {
		    //输出失败原因,大概率是被狗吃了
		    console.log('估计是被狗吃了', reason)
	})
}

//点击页面任何位置异步执行promise
window.onclick = async function foo() {
    let level1 = await new Promise((resolve, reject) => {
        //1、穿拖鞋
	setTimeout(() => {
		resolve('穿拖鞋搞定,')
	}, 1000)
    })
    //拿着第一步的结果,去第二步进行操作
    let level2 = await new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve(level1 + '洗漱搞定,')
	}, 1000)
    })
    let level3 = await new Promise((resolve, reject) => {
	setTimeout(() => {
	    resolve(level2 + '穿衣服搞定,')
	}, 1000)
    })
    let level4 = await new Promise((resolve,xreject)=>{
	setTimeout(()=>{
	    resolve(level3 + '查看“身手要钱”搞定,')
        }, 1000)
    })
	console.log(level4 + ',之后开车去qfedu上班,元气满满的一天')
}

</script>
</head>
<body>
  <button onclick="axiosmethod();">点击1</button>
  <button onclick="promisemethod1();">点击2</button>
  <button onclick="promisemethod2();">点击3</button>
  <button onclick="promisemethod3('https://www.xxx.cn/index/index/abc/');">点击4</button>
  <button onclick="promisemethod4();">点击5</button>
</body>
</html>



博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。