Promise四式击鼓

Promise系列导航

1.Promise本质击鼓传花的游戏
2.Promise四式击鼓
3.Promise击鼓传花
4.Promise花落谁家知多少


前言

👨‍💻👨‍🌾📝记录学习成果,以便温故而知新
  1. Promise系列文章时学习VUE的知识准备,所以就归为VUE系列了。根据MDN的描述,应该是“JavaScript 标准内置对象”,特此说明。
  2. Promise系列文章主要是学习MDN中 Promise的心得体会,MDN地址

Promise对象滥觞于Promise() constructor或者Promise.resolve()亦或者Promise.reject()。

Promise() 构造函数 MDN的说明:

Promise() 构造函数创建 Promise 对象。它主要用于封装尚未支持 Promise 的基于回调的 API。

Promise.resolve() MDN的说明:

Promise.resolve() 静态方法将给定的值转换为一个 Promise。如果该值本身就是一个 Promise,那么该 Promise 将被返回;如果该值是一个 thenable 对象,Promise.resolve() 将调用其 then()方法及其两个回调函数;否则,返回的 Promise 将会以该值兑现。 该函数将嵌套的类 Promise 对象(例如,一个将被兑现为另一个 Promise 对象的 Promise 对象)展平,转化为单个 Promise 对象,其兑现值为一个非 thenable 值。

Promise.reject() MDN的说明:

Promise.reject() 静态方法返回一个已拒绝(rejected)的 Promise 对象,拒绝原因为给定的参数。

以上的说明表明三者都可以原生性的产生Promise 对象,所以“滥觞”一次就很恰如其分。这种原生性也正如“击鼓传花”游戏的开始击鼓。

Promise() 是构造函数,需要用关键字new来调用,而Promise.resolve()与Promise.reject()是静态方法

以上说明来自于MDN“JavaScript 参考>JavaScript 标准内置对象>Promise”,而下面要说的async function来自于“JavaScript 参考>语句和声明>async 函数”。

async function MDN的说明:

async 函数是使用async关键字声明的函数。async 函数是 AsyncFunction 构造函数的实例,并且其中允许使用 await 关键字。async 和 await 关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用 promise。
async 函数还可以被作为表达式来定义。

以上说明虽然有些晦涩,但是大致也能看出,这也能产生Promise。

一、一击鼓——Promise() constructor

1.语法

Promise()语法:

new Promise(executor)

当通过 new 关键字调用 Promise 构造函数时,它会返回一个 Promise 对象。

executor语法:

function executor(resolveFunc, rejectFunc) {
// 通常,executor 函数用于封装某些接受回调函数作为参数的异步操作,比如AJAX调用
}

resolveFunc 和 rejectFunc 也是函数,你可以给它们任何实际的名称。它们的函数签名很简单:它们接受一个任意类型的参数。

2.代码及说明

由于MDN的描述比较晦涩,且感觉很杂乱,下面就直接上代码,看运行效果好了。

(1)代码段:

//这里用了箭头函数
const promise1 = new Promise((resolveFunc, rejectFunc) => {
	console.log("构造函数内:" + (resolveFunc));//这里可以证明确实是函数
	
	resolveFunc("魏紫");
});
const promise2 = new Promise((resolveFunc, rejectFunc) => {
	console.log("构造函数内:" + (rejectFunc));//这里可以证明确实是函数
	
	rejectFunc("姚黄");
});

console.log(promise1);
console.log(promise2);

运行结果:
在这里插入图片描述
从运行结果看,构造函数里的代码是直接执行的,Promise的状态也是一目了然的。另外有传“姚黄”时,没有后续的处理,所以抛出了异常。

(2)代码段:

const promise1 = new Promise((resolveFunc, rejectFunc) => {
	resolveFunc(Promise.resolve("魏紫"));
});
const promise2 = new Promise((resolveFunc, rejectFunc) => {
	resolveFunc(Promise.reject("姚黄"));
});
const promise3 = new Promise((resolveFunc, rejectFunc) => {
	rejectFunc(Promise.resolve("魏紫"));
});
const promise4 = new Promise((resolveFunc, rejectFunc) => {
	rejectFunc(Promise.reject("姚黄"));
});

console.log(promise1);
console.log(promise2);
console.log(promise3);
console.log(promise4);

运行结果:
在这里插入图片描述
请注意红圈中与上面的差异。
感觉这里就体现了MDN中所说

请注意,如果你调用 resolveFunc 或 rejectFunc 并传入另一个 Promise 对象作为参数,可以说该 Promise 对象“已解决”,但仍未“敲定(settled)”。

但是感觉又不全对,rejectFunc调用后Promise对象的状态就是“rejected”。

(3)代码段

const promise1 = new Promise((resolveFunc, rejectFunc) => {
	setTimeout(() => {
		resolveFunc(promise1);
	}, 1000);	
});
const promise2 = new Promise((resolveFunc, rejectFunc) => {
	setTimeout(() => {
		rejectFunc(promise2);
	}, 1000);
});

console.log(promise1);
console.log(promise2);

运行结果:
在这里插入图片描述

结果真如MDN所说

如果resolveFunc被调用时传入了新建的 Promise 对象本身(即它所“绑定”的 Promise 对象),则 Promise对象会被拒绝并抛出一个 TypeError 错误。

不知道 rejectFunc为什么不是TypeError,不知道是否是Uncaught异常先于TypeError发生???

二、二击鼓——Promise.resolve()

1.语法

Promise.resolve(value)

value是要被该 Promise 对象解决的参数。也可以是一个 Promise 对象或一个 thenable 对象。

Promise.resolve() 方法特殊处理了原生 Promise 实例。如果 value 属于 Promise 或其子类,并且 value.constructor === Promise,那么 Promise.resolve() 直接返回 value,而不会创建一个新的 Promise 实例。否则,Promise.resolve() 实际上相当于 new Promise((resolve) => resolve(value)) 的简写形式。

2.代码及说明

还是上代码比较直观一些。

(1)代码段:

Promise.resolve("魏紫").then(
  (value) => {
    console.log(value); // "魏紫"
  },
  (reason) => {
    // 不会被调用
  },
);

//同时满足一下好奇心
console.log(typeof Promise.resolve);//同时满足一下好奇心
console.log(Promise.resolve);//同时满足一下好奇心
console.log(typeof resolveFunc);//同时满足一下好奇心
new Promise((resolveFunc, rejectFunc) => {
	console.log(typeof resolveFunc);//同时满足一下好奇心
	console.log(resolveFunc);//同时满足一下好奇心
	console.log(Promise.resolve == resolveFunc);//同时满足一下好奇心
	console.log(Promise.resolve === resolveFunc);//同时满足一下好奇心
});

运行结果:
在这里插入图片描述
这段代码的传参是最简单的字符串。
重点说一下满足好奇心的这段代码。多少教程或文档中把resolveFunc写成resolve,让人误以为resolve就是Promise.resolve(),实践说明并不是这样的。

(2)代码段:

const p = Promise.resolve([1, 2, 3]);
p.then((v) => {
	console.log(v);
	console.log(v[0]); // 1
});

运行结果:
在这里插入图片描述

这段代码的传参所谓复杂了一点,是数组。

(3)代码段:

const original = Promise.resolve("魏紫");
const cast = Promise.resolve(original);
cast.then((value) => {
  console.log(`值:${value}`);
});
console.log(`original === cast ? ${original === cast}`);
console.log(original);
console.log(cast);

运行结果:
在这里插入图片描述

这段代码中用了一个Promise对象作为了参数,结果正如MDN所说:

Promise.resolve() 方法会重用已存在的 Promise 实例。如果它正在解决一个原生的 Promise,它将返回同一 Promise 实例,而不会创建一个封装对象。

大概也是这个意思:

该函数将嵌套的类 Promise 对象(例如,一个将被兑现为另一个 Promise 对象的 Promise 对象)展平,转化为单个 Promise 对象,其兑现值为一个非 thenable 值。

(4)代码段:

const promise = Promise.resolve(Promise.reject("姚黄"));
console.log(promise);

运行结果:
在这里插入图片描述
这段代码的传参是也是Promise对象,只不过是“姚黄”,一如既往地抛出了异常。

MDN中还其它更复杂情况的说明,感觉层次太深,就不罗列了。

三、三击鼓——Promise.reject()

1.语法

Promise.reject(reason)

reason是该 Promise 对象被拒绝的原因。

Promise.reject() 实际上相当于 new Promise((resolve, reject) => reject(reason)) 的简写形式。

2.代码及说明

还是直接上代码。

(1)代码段:

const promise = Promise.reject(new Error("姚黄")).then(
  () => {
    // 不会被调用
  },
  (error) => {
    console.error(error); // Stacktrace
  },
);
console.log(promise);

运行结果:
在这里插入图片描述
这段代码的传参是个对象,由于then方法的第二个回调函数做了处理,所以并没有像之前代码的运行结果中抛出了异常。之所以输出是红色显示,是console.error的功劳。

(2)代码段:

const p = Promise.resolve("魏紫");
const rejected = Promise.reject(p);
console.log(rejected === p); // false
/*
rejected.then(() => {}, (v) => {
	console.log(v === p); // true
});
*/
rejected.catch((v) => {
	console.log(v === p); // true
});
  console.log(rejected);

运行结果:
在这里插入图片描述

这段代码用了一个Promise对象作为参数,结果表明首先没有像Promise.resolve()返回返回同一 Promise 实例,其次参数原封不用传给了then或者catch方法。正如MDN所说:

与 Promise.resolve 不同,Promise.reject 方法不会重用已存在的 Promise 实例。它始终返回一个新的 Promise 实例,该实例封装了拒绝的原因(reason)。

(3)代码段:

const rejected = Promise.reject(Promise.reject("姚黄"));
console.log(rejected);

运行结果:
在这里插入图片描述
这段代码运行结果表明有两次Uncaught。

四、四击鼓——async function

1.语法

async function name(param0) {
statements
}
async function name(param0, param1) {
statements
}
async function name(param0, param1, /* … ,*/ paramN) {
statements
}

参数并没有什么特别的,是可变长参数,0个或多个参数。
主要是函数体:

包含函数主体的表达式。可以使用 await 机制。

返回值:

返回值是一个 Promise,这个 promise 要么会通过一个由 async 函数返回的值被解决,要么会通过一个从 async 函数中抛出的(或其中没有被捕获到的)异常被拒绝。

由于async function是独立模块,与类或者对象没有关系,所以称其为“函数”,而不是“方法”;而Promise是类,所以其成员都称其为“方法”。

2.代码及说明

还是直接上代码。

(1)代码段:

//主调函数
function test(){
	const p1 = async1();
	const p2 = async2();
	const p3 = async3();
	
	console.log(p1);
	console.log(p2);
	console.log(p3);
	
	p1.then(Wz => console.log("魏紫" + Wz), Yh => console.log("姚黄" + Yh));
	p2.then(Wz => console.log("魏紫" + Wz), Yh => console.log("姚黄" + Yh));
	p3.then(Wz => console.log("魏紫" + Wz), Yh => console.log("姚黄" + Yh));
}

async function async1() {
	return "魏紫1";//不含await
}

async function async2() {
	return await "魏紫2";
}

async function async3() {
	await "魏紫3";
	return "魏紫4";
}

运行结果:
在这里插入图片描述
运行结果表名果然是产生了Promise,说“四击鼓”是恰当的。但是要注意函数体中有无await对Promise造成的影响。

需要强调的是:await关键字只在 async 函数内有效。如果你在 async 函数体之外使用它,就会抛出语法错误 SyntaxError 。

(2)代码段:

//主调函数
function test(){
	async4().catch(() => {}); // 捕捉所有的错误...
}

async function async4() {
  const p1 = new Promise((resolve) => setTimeout(() => resolve("1"), 1000));
  const p2 = new Promise((_, reject) => setTimeout(() => reject("2"), 500));
  const results = [await p1, await p2]; // 不推荐使用这种方式,请使用 Promise.all 或者 Promise.allSettled
  
  return p2;
}

运行结果:
在这里插入图片描述
以上代码及运行结果是为了说明MDN中所说:

1.在 await 表达式之后的代码可以被认为是存在在链式调用的 then 回调中,多个 await 表达式都将加入链式调用的 then 回调中,返回值将作为最后一个 then 回调的返回值。
2.promise 链不是一次就构建好的,相反,promise 链是分阶段构造的,因此在处理异步函数时必须注意对错误函数的处理。
3.在上面代码中,即使在 promise 链中进一步配置了 .catch 方法处理,也会抛出一个未处理的 promise 被拒绝的错误。这是因为 p2 直到控制从 p1 返回后才会连接到 promise 链。

作为对比,看如下代码:

function test48(){
	async5().catch(reason => console.log(reason));
}

async function async5() {
	return Promise.resolve().then(x => Promise.reject("姚黄"));
}

运行结果:
在这里插入图片描述
对比起来看,本人是不认可MDN所说的第3点的内容,本人的观点是await的阻隔造成的。

另外async function模块中使用 async 函数重写 promise 链的内容感觉很有借鉴意义,可参考原文,这里就不啰嗦了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值