接前面的:
- JavaScript引擎与运行时入门小记:在哪些环境下可以运行JavaScript
- JavaScript学习笔记(一):理解概念,一等函数、编程范式、原型、单线程
- JavaScript学习笔记(二):全局对象globalThis,this用法,全局对象们
- JavaScript学习笔记(三):7种原始数据类型,对象类型(引用类型),typeof,原始类型包装器
- JavaScript学习笔记(四):ES6模块ESM,ES6之前CJS,Nodejs如何区分传统脚本与ESM模块文件
- JavaScript学习笔记(五):Event loop 简简单单一个东西,术语太乱了
- JavaScript学习笔记(六):Promise 基本概念,一些简单例子
继续学习JavaScript。
Promise
Promise是现代JavaScript中异步编程的基础。
Promise应该翻译成 承诺?承诺会提供一个值,如果不能提供值会提供错误原因?
概念
Promise是由异步函数返回的一个对象,它表示操作的当前状态。在将Promise返回给调用者的时候,操作通常尚未完成,但是Promise对象提供了方法来响应其最终成功或失败的场景。
Promise 有三种状态:
- pending:待定状态,初始状态。
- fulfilled:已实现状态,表示操作已成功完成。
- rejected:已拒绝状态,表示操作失败。
处于pending状态的Promise最终状态可以是以某个值 进入fulfilled状态或以某个原因(error)进入 rejected状态。当其中任何一种情况发生时,由Promise的then
方法排队的相关处理程序将被调用。如果在附加相应处理程序时Promise已经完成或拒绝,处理程序将被立即调用。
fulfilled或rejected状态都可称为settled(已完成)状态。
构造
Promise只能使用new来构造:
new Promise(executor)
Promise构造函数接受一个执行器函数,这个函数长这样:
function executor(resolveFunc, rejectFunc) {
// 异步操作,例如从服务器获取数据
// 如果成功,调用 resolveFunc,传递结果
// 如果失败,调用 rejectFunc,传递错误信息
}
注:执行器函数是在promise创建时,同步执行的!执行器函数的返回值没有用。
它有两个参数 resolveFunc
和 rejectFunc
,分别用于将 Promise 状态从 Pending 改为 Fulfilled 或 Rejected。
resolveFunc(value)
:将 Promise 的状态从 Pending 转变为 Fulfilled(已实现),并携带一个指定的解决值value
。这表示异步操作成功完成。rejectFunc(reason)
:将 Promise 的状态从 Pending 转变为 Rejected(已拒绝),并携带一个指定的拒绝原因reason
。这表示异步操作遇到了错误或失败。
由于这两个函数是Promise构造时自动生成的,要想在执行器函数的外边执行它,很不方便,需要通过变量先传递出去:
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
为了减轻这个工作,ES2023为Promise引入了静态函数withResolvers
,它可以同时返回创建的promise对象以及对应的resolveFunc
和rejectFunc
:
{ promise, resolveFunc, rejectFunc } = Promise.withResolvers()
不过Node.js尚不支持它。
作为例子,对于Promise创建,可以写一个返回promise的函数,让执行器函数使用setTimeout模拟异步操作:
// 创建一个返回 Promise 的异步函数
function asyncOperation(duration) {
return new Promise((resolveFunc, rejectFunc) => {
// 模拟异步操作,使用WebAPI/Nodejs的 setTimeout()
setTimeout(() => {
const randomNumber = Math.random();
if (randomNumber > 0.5) {
// 模拟异步操作成功
resolveFunc(`Operation succeeded: ${randomNumber}`);
} else {
// 模拟异步操作失败
rejectFunc(new Error(`Operation failed: ${randomNumber}`));
}
}, duration);
});
}
链式操作
Promise通过then()、catch()、finally() 实现链式操作,它们三个都会返回一个新的promise对象:
- promise.prototype.then()
- promise.prototype.catch()
- promise.prototype.finally()
其参数:
then(onFulfilled)
then(onFulfilled, onRejected)
catch(onRejected)
finally(onFinally)
其中,涉及的三个回调函数,长这个样子:
onFulfilled(value)
onRejected(error)
onFinally()
在then()
和catch()
中:如果onFulfilled
不是函数的话,内部它会被替换成(x)=>x
;如果onRejected
不是函数的话,内部会被替换成(x) => { throw x; }
。
注:
catch(onRejected)
是then(null, onRejected)
的简写形式;而finally(onFinally)
和then(onFinally, onFinally)
行为有些像,不管怎样回调都会执行。但不用于then,finally中的回调函数没有参数。
所以,先看then()
好了,看看它返回的新的 Promise 是怎么来的?
不管执行then()
中哪一个回调函数:
回调函数 | 新的 promise | 备注 |
---|---|---|
无返回值 | fulfilled状态,携带值是undefined? | - |
返回一个值 | fulfilled状态,使用这个返回值 | - |
返回promise对象 | 与该promise的状态和值保持一致 | - |
抛出异常 | rejected状态,携带抛出的异常 | - |
在回调函数中,我们可以抛出异常,没有返回值,返回一个值,或者返回另一个promise对象。不管怎样,then()、catch() 都会基于其结果返回一个新的promise对象。不用我们传入执行器函数来手动创建promise对象。
对于finally(onFinally)
返回的promise来说,onFinally的返回值被默认被忽略(除非其返回值是一个rejected状态的promise)。
接前面构造的例子,继续通过例子看看then()
的使用:
// 使用 Promise 进行异步操作处理
asyncOperation(2000)
.then((result) => {
// 在异步操作成功时执行的回调函数
console.log(result);
return 'Additional data'; // 返回一个值,将传递给下一个 then
})
.then((additionalData) => {
// 在上一个 then 方法成功回调函数返回值的基础上执行
console.log(`Received additional data: ${additionalData}`);
})
.catch((error) => {
// 捕获异步操作失败时的错误
console.error(error.message);
})
.finally(() => {
// 不论 Promise 最终状态如何,都会执行的回调函数
console.log('Finally block executed');
});
运行结果:
Operation succeeded: 0.6106660469894274
Received additional data: Additional data
Finally block executed
运行结果:
Operation failed: 0.03309163424930217
Finally block executed
其他成员
Promise还有其他一些成员,都会返回一个新的promise对象:
- Promise.all()
- Promise.any()
- Promise.race()
- Promise.reject()
- Promise.resolve()
前三个都接收一个可迭代的对象(通常是包含多个 Promise 的数组),返回一个新的 Promise。后两个可以看作是用于创建指定状态的Promise,具体看后面例子。
reject 始终会创建新的promise,resolve不一定,而且resolve还可以接受thenable。
例子
抛开文字,通过简单的例子找找感觉:
then() 返回的promise
回调有返回值,p为fulfilled状态,且使用该返回值:
let p = Promise.resolve('1+1=10').then((value) => {
return value.replace('10', '2');
});
p.then((value) => {
console.log(value);
});
// output: 1+1=2
回调无返回值,p为fulfilled状态,且使用undefined:
let p = Promise.resolve('1+1=10').then((value) => {
});
p.then((value) => {
console.log(value);
});
// output: undefined
回调返回promise,p状态与值与回调返回的promise一样:
let p = Promise.resolve('1+1=10').then((value) => {
if (Math.random() > 0.5)
return Promise.resolve('1+1=2');
return Promise.reject('1+1=3');
});
p.then((value) => {
console.log(`resolved: ${value}`);
}).catch((error) => {
console.log(`rejected: ${error}`);
});
// output: resolved: 1+1=2 or rejected: 1+1=3
回调抛出异常,p状态为rejected:
let p = Promise.resolve('1+1=10').then((value) => {
throw new Error('1+1=3');
});
p.then((value) => {
console.log(`resolved: ${value}`);
}).catch((error) => {
console.log(`rejected: ${error}`);
});
// output: rejected: Error: 1+1=3
刚才用了then的onFulfulled,使用onRejected时,结论与前面一样:
let p = Promise.reject('1+1=10').then(null, (value) => {
return "1+1=10";
});
p.then((value) => {
console.log(`resolved: ${value}`);
}).catch((error) => {
console.log(`rejected: ${error}`);
});
// output: resolved: 1+1=10
catch() 返回的promise
与then其实一样
let p = Promise.reject('1+1=10').catch((value) => {
return "1+1=10";
});
p.then((value) => {
console.log(`resolved: ${value}`);
}).catch((error) => {
console.log(`rejected: ${error}`);
});
// output: resolved: 1+1=10
finally() 返回的promise
不受回调返回值影响,与原promise的状态和值都一样
let p = Promise.reject('1+1=10').finally(() => {
})
p.then((value) => {
console.log(`resolved: ${value}`);
}).catch((error) => {
console.log(`rejected: ${error}`);
});
// output: resolved: 1+1=10
回调返回fulfilled的promise,对p没影响
let p = Promise.reject('1+1=10').finally(() => {
return Promise.resolve('1+1=2');
})
p.then((value) => {
console.log(`resolved: ${value}`);
}).catch((error) => {
console.log(`rejected: ${error}`);
});
// output: resolved: 1+1=10
回调返回rejected的promise,对p有影响!!
let p = Promise.reject('1+1=10').finally(() => {
return Promise.reject('1+1=3');
})
p.then((value) => {
console.log(`resolved: ${value}`);
}).catch((error) => {
console.log(`rejected: ${error}`);
});
// output: rejected: 1+1=3
回调抛出异常,和then预期一样
let p = Promise.resolve('1+1=10').finally(() => {
throw new Error('1+1=3');
})
p.then((value) => {
console.log(`resolved: ${value}`);
}).catch((error) => {
console.log(`rejected: ${error}`);
});
// output: rejected:Error: 1+1=3
Promise.reject()生成的promise
Promise.reject()等价于
new Promise((resolveFunc, rejectFunc)=> rejectFunc(reason))
所以:
Promise.reject('1+1=3').catch((reason) => {
console.log('rejected:', reason);
});
new Promise((resolveFunc, rejectFunc)=> rejectFunc('1+1=3')).catch((reason) => {
console.log('rejected:', reason);
});
// rejected: 1+1=3
// rejected: 1+1=3
Reomise.resolve()返回的promise
这个静态函数用于将给定值变成一个promise!
let p = Promise.resolve('1+1=10');
p.then((v) => {
console.log(v);
});
//output: 1+1=10
如果给他一个promise,则会直接返回
let p = Promise.resolve(Promise.reject('1+1=3'));
p.then((value) => {
console.log(`resolved: ${value}`);
}).catch((error) => {
console.log(`rejected: ${error}`);
});
// Output:
// rejected: 1+1=3
额,应该这样
let p0 = Promise.reject('1+1=3');
let p = Promise.resolve(p0);
console.log(p === p0); // true
resolve()还接收thenable。thenable需要实现接收两个回调函数的then()
方法,早期Promise标准化之前有各种promise实现都实现了这个theable协议。
const aThenable = {
then(onFulfilled, onRejected) {
onFulfilled('1+1=10');
}
}
Promise.resolve(aThenable).then((result) => {
console.log(result);
});
// Output: 1+1=10
这个神奇的函数,还能接收和Promise的构造函数长的一样的非promise构造函数
class NotPromise {
constructor(executor) {
executor(
(value) => console.log("Resolved", value),
(reason) => console.log("Rejected", reason),
);
}
}
Promise.resolve.call(NotPromise, "1+1=10"); // Resolved 1+1=10
参考
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise