网站首页 > 博客文章 正文
一、期约
1.1、期约
期约有三种状态:Pending、fulfilled、rejected等,状态是私有的,不能直接检测到,也不能被外部js代码修改。期约状态一旦非Pending了,就不能转换了。
执行器函数:主要负责初始化期约的异步行为和控制状态的最终转换。控制状态主要是依靠resolve和reject函数实现。执行器函数是同步执行的。
let p = new Promise((resolve, reject) => { reject() });
let p1 = Promise.resolve("a");
let p2 = Promise.reject("c"); // Uncaught (in promise) c
let p3 = Promise.resolve(p2);
console.log(p3);// Promise rejected {<rejected>: 'c'}
let p4 = Promise.resolve(p1);
console.log(p4);// Promise fulfilled {<fulfilled>: 'a'}
let p5 = Promise.reject(p1);
console.log(p5);// Promise rejected {Promise {<fulfilled>: 'a'}}
console.log(Promise.reject(Promise.resolve(" b")));//Promise rejected {Promise {<fulfilled>: 'b'}}
期约嵌套时:
如果是resolve嵌套resolve那就是幂等逻辑,
如果是reject嵌套resolve那状态就是reject,拒绝期约的理由就是Promise<fulfilled>。
如果是resolve嵌套reject,结果还是reject的,翻转不了状态。
1.2、then(resolve,reject)返回新的期约
let p = Promise.reject("foo");
let p1 = p.then(null, () => Error("baz"));
setTimeout(console.log, 0, p1);// Promise {<fulfilled>: Error: baz
let p2 = p.then(null, () => {throw 'aaa'});
setTimeout(console.log, 0, p2);// Promise {<rejected>: 'aaa'}
在这个例子中,p1 是通过 p 的 then 方法创建的,该方法中使用了一个拒绝处理函数,但是拒绝处理函数返回了一个新的 Error 对象而不是一个 Promise 对象。根据 Promise 的规范,如果拒绝处理函数返回一个非 Promise 值(比如一个普通的对象、字符串、数字等),则会将这个非 Promise 值作为成功的值来解析,并且返回一个状态为已解决(fulfilled)的 Promise 对象,其值就是拒绝处理函数的返回值。
let p = Promise.reject("foo");
let p1 = p.then(null, () => { });
let p2 = p.then(null, () => undefined);
let p3 = p.then(null, () => Promise.resolve());
setTimeout(console.log, 0, p1);
setTimeout(console.log, 0, p2);
setTimeout(console.log, 0, p3);
let a = Promise.resolve("foo");
let a1 = a.then(() => { });
let a2 = a.then(() => undefined);
let a3 = a.then(() => Promise.resolve());
setTimeout(console.log, 0, a1);
setTimeout(console.log, 0, a2);
setTimeout(console.log, 0, a3);
以上打印的都是Promise {<fulfilled>: undefined}
Promise.prototype.catch(onReject)就相当于then(null, onReject);
Promise.prototype.finally()则是在转换为解决或者拒绝时执行,多数情况表现为父期约的传递。
let p = Promise.reject("foo");
let p1 = p.finally(null, () => { });
let p2 = p.finally(null, () => undefined);
let p3 = p.finally(null, () => Promise.resolve());
setTimeout(console.log, 0, p1);// Promise {<rejected>: 'foo'}
setTimeout(console.log, 0, p2);// Promise {<rejected>: 'foo'}
setTimeout(console.log, 0, p3);// Promise {<rejected>: 'foo'}
?
let a = Promise.resolve("foo");
let a1 = a.finally(() => { });
let a2 = a.finally(() => undefined);
let a3 = a.finally(() => Promise.resolve());
setTimeout(console.log, 0, a1);// Promise {<fulfilled>: 'foo'}
setTimeout(console.log, 0, a2);// Promise {<fulfilled>: 'foo'}
setTimeout(console.log, 0, a3);// Promise {<fulfilled>: 'foo'}
let a4= a.finally(() => {throw "aa"});
setTimeout(console.log, 0, a4);// Promise {<rejected>: 'aa'}
Promise 的非重入特性指的是 Promise 的执行过程是不可中断的,一旦开始执行,就会一直执行到结束,并且期间不会被其他代码打断。具体来说,当一个 Promise 开始执行时,它会一直执行,直到完成(resolve)或拒绝(reject)。在执行过程中,无法中断 Promise 的执行,也无法将其暂停或取消。即使在 Promise 执行过程中发生了异步操作(例如定时器、异步请求等),这些异步操作也无法中断 Promise 的执行,Promise 会一直保持执行状态,直到异步操作完成。
这种特性保证了 Promise 的可靠性和稳定性,确保 Promise 的状态不会被外部因素干扰,从而避免了竞态条件和不确定性的出现。同时,由于 Promise 的执行是非重入的,所以在执行期间无法在 Promise 中插入其他任务或代码片段,保证了 Promise 的原子性和一致性。
在 Java 中,非重入通常指的是一种锁的行为特性。非重入锁是一种锁机制,它不允许同一个线程多次获取同一个锁。换句话说,如果一个线程已经持有了某个锁,再次尝试获取该锁会导致线程阻塞或抛出异常。
在 Java 中,常见的非重入锁包括使用 synchronized 关键字定义的同步块以及 ReentrantLock 类的非公平锁模式。这些锁在同一个线程多次尝试获取同一个锁时,会阻塞该线程或抛出异常,以避免锁的重入。非重入锁的设计有助于避免并发编程中的死锁和饥饿问题,但同时也会增加代码的复杂性和性能开销。
let a = Promise.resolve("foo");
a.then(() => { console.log("a resolved") });
console.log("test ")
日志打印这里也是test先打印,后续才打印a resolved,因为处理程序会等到运行的消息队列让它出列时才执行。
1.3、传递解决值和拒绝理由
let a = Promise.resolve("foo");
a.then((value) => { console.log(value) }); // foo
?
let b = Promise.reject("baz");
b.catch((value) => { console.log(value) });// baz
1.4、期约拒绝
let b = Promise.reject("baz");
b.catch((value) => console.log(value)) // baz
.then(() => console.log("resolved"));// resolved
为啥catch之后的then只写了resolve方法也能打印,是因为catch方法的返回值是非 Promise 值,那么就会作为成功的值来解析,并且返回一个状态为已解决(fulfilled)的 Promise 对象,后面使用then就可以捕捉了.
1.5、期约连锁与期约合成
then、catch、finally使用。
期约图:有向无环图图,就是二叉树,层序遍历。期约的处理程序是先添加到消息队列,然后才逐个执行,因此构成层序遍历。
let A = new Promise((resolve, reject) => {
console.log("A");
resolve();
})
let B = A.then(() => console.log("B"));
let C = A.then(() => console.log("C"));
let D = B.then(() => console.log("D"));
let E = B.then(() => console.log("E"));
// A B C D E F
// A
// B C
// D E
1.6、Promise.all和Promise.race
let all = Promise.all([
Promise.resolve(3),
new Promise((resolve, reject) => {
console.log("a")
setTimeout(resolve, 2000);
})
])
let p = all.then(() => console.log("b"));
a打印之后,间隔2s才打印b
let all = Promise.all([
Promise.reject(3),
new Promise((resolve, reject) => {
console.log("a")
setTimeout(resolve, 2000);
})
])
let p = all.then(null, () => console.log("b"));
a打印之后,很快打印了b,因为只要有一个reject,那整个就reject,不会执行完后面resolve了。
let all = Promise.all([
new Promise((resolve, reject) => {
reject();
console.log("b");
}), Promise.reject(3)
])
let p = all.then(null, (value) => console.log(value)); // undefined
- 在 JavaScript 中,Promise.all() 和 Promise.race() 是两种常用的 Promise 组合方法,它们有以下区别:
Promise.all():
- 接收一个由多个 Promise 对象组成的可迭代对象(如数组),并返回一个新的 Promise 对象。
- 当传入的所有 Promise 对象都成功(即状态变为 resolved)时,返回的 Promise 对象的状态变为 resolved,并且将所有 Promise 对象的结果组成的数组作为值传递给成功的回调函数。
- 如果传入的任何一个 Promise 对象失败(即状态变为 rejected),则返回的 Promise 对象的状态会立即变为 rejected,并且失败的 Promise 对象的结果会作为值传递给失败的回调函数。
Promise.race():
- 接收一个由多个 Promise 对象组成的可迭代对象(如数组),并返回一个新的 Promise 对象。
- 当传入的多个 Promise 对象中有任意一个状态发生变化时(无论是成功还是失败),返回的 Promise 对象的状态就会立即变为与该变化一致,并且将第一个状态变化的 Promise 对象的结果作为值传递给对应的回调函数。
简而言之,Promise.all() 用于等待多个 Promise 对象全部完成,然后返回一个包含所有结果的新 Promise 对象;而 Promise.race() 则是用于等待多个 Promise 对象中的任意一个完成,并返回第一个完成的 Promise 对象的结果。
1.7、串行期约合成
function add3(x) {
return x + 3;
}
function add5(x) {
return x + 5;
}
function add7(x) {
return x + 7;
}
function add15(x) {
return add3(add5(add7(x)));
}
function add15(x) {
return Promise.resolve(x).then(add3).then(add5).then(add7);
}
add15(0).then((x) => console.log(x));// 15
function add15(x) {
return [add3, add5, add7].reduce((promise, fn) => promise.then(fn), Promise.resolve(x));
}
add15(0).then((x) => console.log(x));// 15
function compose(...fns) {
return (x) => fns.reduce((promise, fn) => promise.then(fn), Promise.resolve(x));
}
compose(add3,add5,add7)(10).then(console.log); // 25
来看看最后一个方法compose,返回是一个函数,那这个compse调用 compose(add3,add5,add7)返回一个函数,函数的参数传入10结果就是25;
1.8、期约的拓展之取消
Promise规范里期约是不能取消的,但是可以模拟取消。
class CancelToken {
constructor(cancelFn) {
this.promise = new Promise((resolve, reject) => {
console.log("aaa")
cancelFn(() => {
setTimeout(console.log, 0, "delay cancelled");
resolve();
});
});
}
}
const startButton = document.querySelector("#start");
const cancelButton = document.querySelector("#cancel");
function cancellable(delay) {
console.log("set delay");
return new Promise((resolve, reject) => {
const id = setTimeout((() => {
console.log("delay resolve");
resolve();
}), delay);
const cancelToken = new CancelToken((callback) => {
cancelButton.addEventListener("click", callback);
})
cancelToken.promise.then(() => clearTimeout(id));
})
}
startButton.addEventListener("click", () => cancellable(5000));
点击start按钮,创建了一个期约A,期约A初始化时定义了一个5s的定时器,同时创建了一个CancelToken对象,给取消按钮设置了监听,且CancelToken对象创建时会创建一个期约B。一旦取消按钮点击,就会触发B期约解决,就会触发clearTimeout,那么A期约就一直是一个Pending状态。
1.9、期约的拓展之进度通知
期约是不支持进度追踪的,但是可以通过拓展实现。
class TrackablePromise extends Promise {
constructor(executor) {
const notifyHandlers = [];
super((resolve, reject) => {
return executor(resolve, reject, (status) => {
notifyHandlers.map((handler) => handler(status));
})
})
this.notifyHandlers = notifyHandlers;
}
notify(handler) {
this.notifyHandlers.push(handler);
return this;
}
}
let p = new TrackablePromise((resolve, reject, notify) => {
function countDown(x) {
if (x > 0) {
notify(`${20 * x}%remanining`);
setTimeout(() => countDown(x - 1), 1000);
} else {
resolve();
}
}
countDown(5);
})
p.notify((x) => setTimeout(console.log, 0, "progress", x));
p.then(() => setTimeout(console.log, 0, "complete"));
- handler 是一个函数,用于处理任务的进度通知。当执行器函数中调用 notify 方法时,会将该函数作为参数传入,并存储在 notifyHandlers 数组中。当任务的进度发生变化时,会依次调用 notifyHandlers 数组中的每个处理函数,将当前任务的状态传递给它们。
- status 是描述任务进度的消息。在执行器函数中调用 notify 方法时,会将当前任务的状态作为参数传入,并传递给通知处理函数。在这个例子中,任务的状态是一个表示任务剩余进度的消息字符串,例如 "20% remaining"。
因此,handler 是用于处理任务进度通知的函数,而 status 是描述任务进度的消息。
二、异步函数
2.1、异步函数async、await
async就只是一个标志符,真正起作用的是await。javaScript运行时在碰到await关键字时,会记录在哪里暂停执行。等到await右边的值可用了,Javascript运行时会向消息队列中推送一个任务,这个任务会恢复异步函数的执行。
async function foo() {
}
foo().then((i) => console.log(i)); // undefined
async function bar() {
return 1;
}
bar().then((i) => console.log(i));// 1
async修饰的函数,返回值会包装成一个fulfilled的期约返回。
async function foo1() {
throw 4;
}
foo1().catch((i) => console.log(i)); // 4
async function foo() {
Promise.reject(3);
console.log(5); // 5
}
foo().catch((i) => console.log(i)); // Uncaught (in promise) 3
async function bar() {
await Promise.reject(4);
console.log(6); // 这个不执行
}
bar().catch((i) => console.log(i));// 4
拒绝的期约可以被await 解包之后,被catch捕获。
在 bar 函数中,await 关键字会暂停函数的执行,直到 Promise 被解决或拒绝。在这种情况下,Promise.reject(4) 被拒绝了,所以 await 后面的代码不会执行。因此,console.log(6) 不会被执行。
async function foo(){
console.log(await Promise.resolve(9));
}
async function bar(){
console.log(await 8);
}
foo();
bar();
执行顺序打印的是9 8,而不是8 9 。
来看看kt和js中使用async和await的例子:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function fetchUserData() {
await delay(1000); // 模拟耗时操作
return "John Doe";
}
async function main() {
try {
console.log("Waiting for user data...");
const userData = await fetchUserData(); // 等待异步操作的结果
console.log("User data received:", userData);
} catch (error) {
console.error("Error fetching user data:", error);
}
}
main();
import kotlinx.coroutines.*
fun main() {
// 启动一个协程
GlobalScope.launch {
val result = async { fetchUserData() } // 启动一个异步任务
println("Waiting for user data...")
val userData = result.await() // 等待异步任务的结果
println("User data received: $userData")
}
Thread.sleep(2000) // 主线程等待协程执行
}
suspend fun fetchUserData(): String {
delay(1000) // 模拟耗时操作
return "John Doe"
}
怎么感觉kt还复杂一些,用了suspend呢。
2.2、异步函数策略
2.2.1、实现sleep
function sleep(delay) {
return new Promise((resolve, reject) => {
setTimeout(resolve, delay);
})
}
async function foo() {
await sleep(1000)
console.log("job");
}
foo();
2.2.2、串行执行期约与利用平行执行
async function delay(id) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`${id} finished`);
resolve();
}, 1000)
})
}
async function foo(){
await delay(1);
await delay(2);
await delay(3);
await delay(4);
console.log("complete");
}
foo();
async function delay(id) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`${id} finished`);
resolve();
}, 1000)
})
}
async function foo(){
const p1 = delay(1);
const p2 = delay(2);
const p3 = delay(3);
const p4 = delay(4);
await p1;
await p2;
await p3;
await p4;
console.log("complete");
}
foo();
第一个例子串行执行期约。
第二个例子可以实现平行加速,因为预先创建Promise,在创建p1、p2、p3、p4等已经开始setTimeout了,await就只是取值而已。
猜你喜欢
- 2024-10-11 JavaScript,ES6,Promise对象,异步编程的一种解决方案,代码
- 2024-10-11 使用 Matter.js 创建物理模拟:牛顿摆
- 2024-10-11 一首歌带你搞懂Promise(歌曲promise)
- 2024-10-11 如何用Vue3和p5.js绘制一个交互式波浪图
- 2024-10-11 IT技术栈:Javascript中Promise的pending、fulfilled和rejected
- 2024-10-11 Node.js中的Promise:回调的替代方案
- 2024-10-11 我终于真正理解 Promise 了!(promise 的理解)
- 2024-10-11 探究JS中Promise函数then的奥秘(js中promise什么意思)
- 2024-10-11 关于js中的promise,与其说是一种语法还不如说是一种思想!
- 2024-10-11 前端-JavaScript异步编程中的Promise
你 发表评论:
欢迎- 最近发表
-
- 给3D Slicer添加Python第三方插件库
- Python自动化——pytest常用插件详解
- Pycharm下安装MicroPython Tools插件(ESP32开发板)
- IntelliJ IDEA 2025.1.3 发布(idea 2020)
- IDEA+Continue插件+DeepSeek:开发者效率飙升的「三体组合」!
- Cursor:提升Python开发效率的必备IDE及插件安装指南
- 日本旅行时想借厕所、买香烟怎么办?便利商店里能解决大问题!
- 11天!日本史上最长黄金周来了!旅游万金句总结!
- 北川景子&DAIGO缘定1.11 召开记者会宣布结婚
- PIKO‘PPAP’ 洗脑歌登上美国告示牌
- 标签列表
-
- ifneq (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)