本文是在阅读 《promises-book》过程中, 记录了个人觉得在 Promise 使用中,容易被忽视但又重要的细节。
每一个知识点,都通过编写对应的测试代码,来验证和加深印象
读者朋友可以先思考标题或想下代码会输出什么,再对比控制台输出的结果,理解和记忆更深刻^_^
其实是 18年7月记录的个人笔记,现在重新整理分享出来哈
您的点赞和评论,是我继续写作的动力~~~
一、构造函数中执行顺序是怎样的?
测试代码:
function testConstructor() {
console.log('1. Init');
const promise = new Promise((resolve) => {
resolve('4. Promise Then');
console.log('2. Call in Promise constructor paremeter');
});
promise.then((msg) => {
console.log(msg);
});
console.log('3. End');
}
testConstructor();
输出结果:
"1. Init"
"2. Call in Promise constructor paremeter"
"3. End"
"4. Promise Then"
解释说明:
Promise 构造函数传进的函数会立即执行, 相当于如下
console.log('1. Init');
(resolve) => {
resolve('4. Promise Then');
console.log('2. Call in Promise constructor paremeter');
})();
console.log('3. End');
先打印 1. Init 后紧接着 2. Call in Promise constructor paremeter,3. End 。
浏览器执行完宏任务后,会执行微任务,执行 promise.then 里的回调函数,console.log(msg); , 即 4. Promise Then
二、取消一个 promise
取消一个 promise 的方式有什么呢?
整理了两种方式,读者朋友可以继续补充、赐教哈
其实并没有真正地停止原有 promise 的执行,而是通过让 resolve 提前执行,或 封装原有Promise(Promise.race), 实现了取消的效果
方式一 cancelToken
测试代码:
const source = {
cancelToken: null,
}
function cacelPromise1(source) {
const p1 = new Promise(function(resolve, reject) {
setTimeout(() => {
resolve('Fullfill after 3s');
}, 3000);
console.log('1. refer source.cancelToken to resolve');
source.cancelToken = resolve;
});
// mResolve('Fullfill at once');
p1.then((msg) => {
console.log(msg);
})
}
cacelPromise1(source);
console.log('2. source.cancelToken...');
source.cancelToken('3. Promise resolve before Fullfill after 3s');
输出结果:
"1. refer source.cancelToken to resolve"
"2. source.cancelToken..."
"3. Promise resolve before Fullfill after 3s"
解释说明:
通过让 source.cancelToken 指向 resolve, 在等待 3s 的 setTimeout 回调执行以前,执行了 source.cancelToken('3. Promise resolve before Fullfill after 3s');, 即 resolve('3. Promise resolve before Fullfill after 3s')
这样 setTimeout 3s 到了 再进行 resovle, 也不会影响 p1.then 中 msg 的数据
注意:传入 cacelPromise1 的参数使用 source.cancelToken,而没有直接使用 cancelToken ,是为了利用函数调用时,参数的传递是浅复制的特性。
提示:多次调用 resolve ,也不会影响
p1.then中msg的数据,可以参考下文
方式二 Promise.race
测试代码
// how to cacel a promise
function cacelPromise2() {
const p1 = new Promise(function(resolve, reject) {
setTimeout(() => {
resolve('Fullfill after 3s');
}, 3000);
});
const p2 = new Promise((resolve) => {
setTimeout(() => {
resolve('Ahead');
}, 100);
})
Promise.race([p1, p2]).then((value) => {
console.log(value);
})
}
cacelPromise2();
输出结果
"Ahead"
三、多次调用 resolve 和 then
测试代码:
function testThenRevoke() {
console.log('1. Init');
const promise = new Promise((resolve) => {
resolve('Resolve First');
resolve('Resove Second');
console.log('2. Call in Promise constructor');
});
promise.then((value) => {
console.log('Then First: ', value);
});
promise.then((value) => {
console.log('Then Second: ', value);
});
console.log('3. End');
};
testThenRevoke();
输出结果:
"1. Init"
"2. Call in Promise constructor"
"3. End"
"Then First: Resolve First"
"Then Second: Resolve First"
解释说明:
多次调用 resolve, 只接收第一个 resolve 的调用
四、then 方法的第二个参数
测试代码:
function testReject() {
console.log('1. Init');
const promise = new Promise((resolve) => {
undefined.key;
console.log('Call in Promise constructor');
});
promise
.then(
function onFulfilled(value) {
console.log('Then onFulfilled: ', msg);
},
function onRejected(reason) {
console.log('3. Then onRejected: ' + reason);
// throw new Error(reason);
},
)
.catch((error) => {
// 传进 Promise catch 的回调将不会执行
// 因为 promise onRejected 默认会 throw new Error(reason);
console.log('Catch: ' + error);
});
console.log('2. End');
};
testReject();
输出结果:
"1. Init"
"2. End"
"3. Then onRejected: TypeError: Cannot read property 'key' of undefined"
解释说明:
undefined.key 触发异常后,后面的 console.log('Call in Promise constructor') 就不会再执行。
如果 promise.then 没传入第二个参数 onRejected,就会进入 promise.catch,因为 promise 默认的 onRejected 默认会 throw new Error(reason)。
测试代码中传进 onRejected, 又没有 throw new Error(reason), 后面的 catch 就不会执行了。
function onRejected(reason) {
console.log('3. Then onRejected: ' + reason);
// throw new Error(reason);
}
五、Promise.resolve
测试代码:
function testPromiseResolve() {
const p1 = new Promise(function(resolve) {
console.log('resolve begin');
setTimeout(() => {
resolve(42);
}, 3000);
console.log('resolve end');
});
const p2 = Promise.resolve(42);
const p3 = Promise.resolve(p1);
console.log('p1 === p2: ' + (p1 === p2));
console.log('p1 === p3: ' + (p1 === p3));
}
testPromiseResolve();
输入结果:
"resolve begin"
"resolve end"
"p1 === p2: false"
"p1 === p3: true"