深入浅出JS—17 Promise产生背景与使用方法

一. Promise的产生背景

Promise是ES6中新提出的类,每一个新方法的提出都是为了解决已有痛点。Promise为处理异步事件提供了很好的解决方案

场景

在开发中,通常异步请求函数和处理数据函数并不在同一个文件中,在request.js中发起异步请求,得到返回结果

// 发起异步请求
function requestData(url) {
	setTimeout(()=>{
		if(url === 'correctURL'){
			let value = ['abc', 'cba', 'nba'];
		}else{
			let reason = "please check your internet";
		}
	}, 1000)
}

在index.js中调用请求函数如下,如何得到返回数据结果呢

requestData("correctURL");

返回值传递❌

直接在函数中将得到的值返回,但是有问题,由于请求数据是异步操作,所以无论如何都会返回undefined。

function requestData(url) {
    let result;
	setTimeout(()=>{
		if(url === 'correctURL'){
			result= ['abc', 'cba', 'nba'];
		}else{
			result = "please check your internet";
		}
	}, 1000)
	
	return result; //undefined;
}

// index.js
const result = requestData("correctURL"); // undefined

回调函数✔️

// request.js
function requestData(url, onSuccess, onFail) {
	setTimeout(()=>{
		if(url === 'correctURL'){
			let value = ['abc', 'cba', 'nba'];
			onSuccess(value);
		}else{
			let reason = "please check your internet";
			onFail(reason)
		}
	}, 1000)
}

在index.js中,利用回调函数的入参拿到异步请求返回结果

requestData(
  "coderwhy",
  (value) => {
    console.log("请求成功", value);
  },
  (reason) => {
    console.log("请求失败", reason);
  }
);

回调函定义是在index.js文件中;而调用是在request.js异步函数中
回调函数定义和调用的位置不同,相当于把一段数据处理逻辑通过回调函数的方式注入了异步函数,拿到数据之后,执行数据处理
在这里插入图片描述
回调函数的弊端

  • 如果是自己封装的requestData,需要设计好回调函数,并按要求使用
  • 如果是别人封装的requestData,需要去看源码或者文档,才知道如何使用回调函数获取结果
  • 回调函数相当于控制反转,调用函数的时机和权利交给别人不安全
  • 回调函数这种非顺序的方式和大脑思考方式相悖

Promise规范✔️👍

用Promise类型对象来作为承诺,用同步的方式写异步代码。

// request.js
function requestData(url) {
	const promise = new Promise((resolve, reject) => {
		setTimeout(()=>{
		if(url === 'correctURL'){
			let value = ['abc', 'cba', 'nba'];
			resolve(value);
		}else{
			let reason = "please check your internet";
			reject(reason)
		}
	}, 1000)
	})

	return promise;
}

异步函数也有返回值,返回一个promise承诺,拿到承诺之后,可以通过then方法执行返回成功的逻辑,catch方法执行返回失败的逻辑。
调用异步函数和调用处理异步结果的函数都由index.js控制执行,控制权掌握在自己手里。

// index.js
const promise = requestData("correctURL");

promise.then(res=>{
	console.log("请求成功", value);
}).catch(err => {
	console.log("请求失败", err);
})

二. Promise的基本使用

Promise的使用需要遵循一套统一的规范,看起来很复杂,其实拆解起来并不难

理解关键

  • Promise是一个类,要创建对象就要调用new关键字,传入的参数为一个函数executor
  • executor函数有两个入参,为两个回调函数resolve,reject,它们已经在Promise类内部实现好了
  • executor函数传入之后会被立即执行
const promise = new Promise((resolve, reject) => {
	// 一些操作
	resolve('value');
	reject('reason');
})

Promise对象的三种状态

  • pending:悬而未决,还没执行resolve
  • fulfilled:已经完成
  • rejected:已被拒绝

状态一旦由pending变为fulfilled或rejected就不会再改变

const promise = new Promise((resolve, reject) => {
	resolve('value'); // pending -> fulfilled
	reject('reason'); //在这里不会执行,状态一经确定,不会改变
	console.log('aaaaaa'); // 会执行
})

Promise对象方法

1 then方法

执行时机:在promise对象状态为fulfilled/rejected时执行
入参:一个(两个)回调函数
返回值:返回一个promise对象,具体取决于onFulfilled或onRejected的返回值

const onFulfilled = res => console.log('成功', res); // res对应着resolve中的参数
const onRejected = err => console.log('失败', err); // err入参对应着reject中的参数

promise.then(onFulfilled, onRejected);
promise.then(onFulfilled)

2 catch方法

在promise对象状态为rejected时执行, 入参一个回调函数
回调函数onRejected入参对应着reject中的参数

const onRejected = err => console.log('失败', err);
promise.catch(onRejected)

3 finally方法

无论状态是什么,最后都会执行finally
入参一个回调函数 onFinally,
回调函数 onFinally没有入参

const onFinally= () => console.log('最后');
promise.catch(onFinally)

Promise类方法

1 resolve

将常量转为Promise对象,如果入参不是常量,而是Promise对象或者thenable的值,那么返回Promise的resolve入参取决于他们的resolve入参

Promise.resolve('aaa')
// 等于下列代码
new Promise((resolve, reject) => {
	resolve('aaa');
})

2 reject

返回一个Promise对象,如果入参不是常量,而是Promise对象或者thenable的值,那么返回Promise的reject入参取决于他们的reject入参

const promise = Promise.reject('rejected message');
// 等于下列代码
const promise1 = new Promise((resolve, reject) => {
  reject('rejected message')
})

3 all & allSettled

Promise.all:多个promise对象状态变为fulfilled时,才会返回, 如果有一个返回值为reject,所有都会中断

Promise.allSettled:多个对象状态都敲定后,就会返回,返回值有状态标识以及内容

  • 参数以数组方式传入
  • 返回值也是数组,顺序与入参相同,与返回时间无关
  • 入参类型不是Promise对象,会借助Promise.resolve转为Promise对象
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(111);
  }, 2000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    //resolve(222);
    reject('p2 reject')
  }, 1000);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(333);
  }, 3000);
});

Promise.all()

// 注意:'aaa'相当于Promise.resolve('aaa')
const p4 = Promise.all([p1, p2, p3, "aaa"]);

p4.then((res) => {
  console.log("res: ", res); 
}).catch((err) => {
  console.log("err: ", err);
});
// err: p2 reject

Promise.allSettled()

const p4 = Promise.allSettled([p1, p2, p3, "aaa"]);

p4.then((res) => {
  console.log("res: ", res); 
}).catch((err) => {
  console.log("err: ", err);
});
// res
res:  [
  { status: 'fulfilled', value: 111 },        
  { status: 'rejected', reason: 'p2 reject' },
  { status: 'fulfilled', value: 333 },        
  { status: 'fulfilled', value: 'aaa' }       
]

4 race & any

Promise.race:返回状态最先敲定的值,可以是resolve也可以是reject值

Promise.any:返回状态最先变为fulfilled的resolve值

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(111);
  }, 2000);
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('p2 reject')
  }, 1500);
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(333);
  }, 3000);
});

Promise.race()

// 注意:reject和resolve公平竞争,谁快谁先返回
// 虽然程序没停止,但是p4已经有结果了
const p4 = Promise.race([p1, p2, p3]);

p4.then((res) => {
  console.log("res: ", res);
}).catch((err) => {
  console.log("err: ", err);
});
// err: p2 reject

Promise.any()

const p4 = Promise.any([p1, p2, p3]);

p4.then((res) => {
  console.log("res: ", res);
}).catch((err) => {
  console.log("err: ", err.errors);
});
// res: 111
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值