精读《JavaScript 高级程序设计 第4版》第11章 期约与异步函数(一)
11.1 期约
期约是对尚不存在结果的一个替身。ECMAScript 6增加了对Promises/A+规范的完善支持,即Promise类型,成为了主导性的异步编程机制。
11.1.1期约基础
ECMAScript 6新增的引用类型Promise,可以通过new操作符来实例化。创建新期约时需要传入执行器(executor)函数作为参数。下面用一个空函数对象来应付一下解释器(若不提供执行器函数则会抛出SyntaxError):
let p = new Promise(() => {});
setTimeOut(console.log, 0, p); // Promise <pending>
1、期约状态机
期约是一个有状态的对象,拥有三种状态:pending(待定)、fulfilled(兑现) / resolved(解决)、rejected(拒绝)。
待定(pending):是期约的初始状态,既没有被兑现,也没有被拒绝。在待定状态下可以落定为另外两种状态。一旦落定后就不可改变,是不可逆的。
兑现(fulfilled):有时也直接称为“已解决(resolved)”。意味着操作成功完成。此时会有一个永久的私有内部值(value),不可修改。内部值可选,默认为undefined。
拒绝(rejected):意味着操作失败。此时会有一个永久的理由(reason)(通常是 Error 对象),该值是私有的内部值,不可修改。理由可选,默认为undefined。
2、通过执行函数控制期约状态
由于期约的状态是私有的,所以只能在内部进行操作。内部的操作在期约的执行器函数中完成。执行期函数主要有两个职责:初始化期约的异步行为和控制状态的转换。其中控制状态的转换是通过调用它的两个函数参数实现的。这两个函数参数通常命名为resolve()和reject()。调用resolve()会把状态切换为兑现,调用reject()会把状态切换成拒绝,另外reject()也会抛出错误。
const myPromise = new Promise((resolve, reject) => {
// 这里是执行器(executor)函数,由 JavaScript 引擎提供,立即执行。
// 模拟异步工作
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('Operation succeeded!'); // 将状态由 pending 变为 fulfilled
} else {
reject(new Error('Operation failed!')); // 将状态由 pending 变为 rejected
}
}, 1000);
});
无论resolve()和reject()哪个被调用了,状态转换都无法撤销了,继续修改状态会静默失败:
let p = new Promise((resolve, reject) => {
resolve();
reject(); //没有效果
})
setTimeout(console.log, 0, p); // Promise <resolved>
为避免期约卡在待定状态,可以添加一个定时退出功能:
let p = new Promise((resolve, reject) => {
setTimeout(reject, 10000); //10秒后调用reject()
//执行函数的逻辑
})
setTimeout(console.log, 0, p); // Promise <pending>The promise rejected with the reason "undefined".
setTimeout(console.log, 11000, p); // 11秒后再检查状态:Promise <rejected>,会抛出The promise rejected with the reason "undefined".
3、Promise.resolve()
可以通过调用Promise.resolve()静态方法,实例化一个解决的期约。下面两个期约实例实际上是一样的:
let p1 = new Promise((resolve, reject) => resolve());
let p2 = Promise.resolve();
Promise.resolve() 会根据传入的 参数 类型有不同的处理方式,多余的参数会被省略:
情况一:传入普通值(非 Promise 对象)
返回一个已兑现的 Promise,其兑现值就是这个普通值。
// 传入字符串
const p1 = Promise.resolve('Hello');
p1.then(value => console.log(value)); // "Hello"
// 传入对象
const p2 = Promise.resolve({ name: 'John', age: 30 });
p2.then(value => console.log(value)); // {name: "John", age: 30}
// 传入数组
const p3 = Promise.resolve([1, 2, 3]);
p3.then(value => console.log(value)); // [1, 2, 3]
// 甚至传入 undefined
const p4 = Promise.resolve();
p4.then(value => console.log(value)); // undefined
情况二:传入 Promise 对象
直接返回这个 Promise 对象本身(幂等操作)。
const originalPromise = new Promise(resolve => {
setTimeout(() => resolve('Original'), 1000);
});
const wrappedPromise = Promise.resolve(originalPromise);
console.log(originalPromise === wrappedPromise); // true
// 两个 Promise 实际上是同一个对象
wrappedPromise.then(value => console.log(value)); // 1秒后输出 "Original"
4、Promise.reject()
Promise.reject() 用于创建一个立即拒绝(rejected) 的 Promise 对象。Promise.reject() 总是返回一个立即拒绝的 Promise,拒绝原因就是你传入的 参数。
const rejectedPromise = Promise.reject(new Error('Something went wrong!'));
rejectedPromise.catch(error => {
console.error(error.message); // "Something went wrong!"
});
但是不同于Promise.resolve()的幂等逻辑,当给它传入一个期约对象,这个期约会成为它返回的拒绝期约的理由:
setTimeout(console.log, 0, Promise.reject(Promise.resolve()));
// Promise <rejected>: Promise <resolved>更多推荐



所有评论(0)