读完此文对promise一定会有新的认识。
文章目录
- promise有三种状态:
pending
(进行中)、fulfilled
(已成功)和rejected
(已失败);- promise状态改变只有两种可能:从
pending
变为fulfilled
和从pending
变为rejected
。状态一旦改变,就不会再变;- 为了方便描述,一般将
fulfilled
状态称为resolved
。
1. 如何改变promise的状态?
- (1)
resolve(value)
:如果当前是pending
,则变为resolved
:
const p1 = new Promise((resolve, reject) => {
resolve('success');
});
console.log(p1); // Promise {<fulfilled>: 'success'}
- (2)
reject(reason)
:如果当前是pending
,则变为rejected
:
const p2 = new Promise((resolve, reject) => {
reject('err');
});
console.log(p2); // Promise {<rejected>: 'err'}
- (3) 抛出异常:如果当前是
pending
,则变为rejected
:
const p3 = new Promise((resolve, reject) => {
throw new Error('err');
});
console,.log(p3) // Promise {<rejected>: Error: err
总结:改变promise的状态有三种方式(调用resolve、调用reject和抛出异常)。
2. 一个promise同时指定多个成功/失败回调,都会调用吗?
- 成功的情况
const p = new Promise((resolve, reject) => {
resolve('success');
});
p.then((value) => {
console.log('value1:', value);
});
p.then((value) => {
console.log('value2:', value);
});
p.then((value) => {
console.log('value3:', value);
});
/* 执行结果:
value1: success
value2: success
value3: success
*/
- 失败的情况
const p = new Promise((resolve, reject) => {
reject('err');
});
p.then(
(value) => {},
(reason) => {
console.log('reason:1', reason);
},
);
p.then(
(value) => {},
(reason) => {
console.log('reason:2', reason);
},
);
p.then(
(value) => {},
(reason) => {
console.log('reason:3', reason);
},
);
/* 执行结果:
reason:1 err
reason:2 err
reason:3 err
*/
总结:一个promise同时指定多个成功/失败回调函数,当promise改变为对应的状态时都会调用。
3. promise的状态改变和回调函数的指定谁先谁后?
我们知道promise在状态改变时会调用对应的回调函数,但在不同的情况下两种还是有先后顺序的。
- 先指定回调函数,后改变状态的情况:
const p = new Promise((resolve, reject) => {
// 执行异步任务,这里用setTimeout模拟,实际上可以是ajax请求
setTimeout(() => {
resolve('success'); // 2. 后改变状态(同时指定数据),异步执行回调函数
}, 1000);
});
p.then((value) => { // 1. 先指定回调函数,保存当前指定的回调函数(代码底层)
console.log('value:', value);
});
- 先改变状态,后指定回调函数的情况:
const p = new Promise((resolve, reject) => {
resolve('success'); // 1. 先改变状态(同时指定数据)
});
p.then((value) => { // 2. 后指定回调函数,异步执行回调函数
console.log('value:', value);
});
或者:
const p = new Promise((resolve, reject) => {
// 执行异步任务,这里用setTimeout模拟,实际上可以是ajax请求
setTimeout(() => {
resolve('success'); // 1. 先改变状态(同时指定数据)
}, 1000);
});
setTimeout(() => {
p.then((value) => { // 2. 后指定回调函数,异步执行回调函数
console.log('value:', value);
});
}, 1001); // 时间比上面晚一点即可
总结:
- 两者都有可能,一般情况(执行异步任务)时先指定回调再改变状态;
- 如何先改变状态再指定回调函数?
- 在执行器中直接调用resolve/reject
- 延迟更长时间再调用then
- 什么时候才得到数据?
- 若先指定回调,那当状态改变时,回调函数就会执行,从而得到数据
- 若先改变状态,那当指定回调时,回调函数就会执行,从而得到数据
4. promise.then 返回的新promise的结果状态由什么决定?
我们知道promise是支持链式调用的,比如下面的情况:
const p = new Promise((resolve, reject) => {
resolve('success');
});
p.then(
(value) => {
console.log('value1:', value);
},
(reason) => {
console.log('reason1:', reason);
},
)
.then(
(value) => {
console.log('value2:', value);
},
(reason) => {
console.log('reason2:', reason);
},
)
.then(
(value) => {
console.log('value3:', value);
},
(reason) => {
console.log('reason3:', reason);
},
);
/* 执行结果:
value1: success
value2: undefined
value3: undefined
/*
为什么后面两个回调的执行结果是undefined
呢,因为新的promise的结果状态是由then()
指定的回调函数执行的结果决定的。可以看到第一个then方法中回调函数没有指定返回值,那么就会默认返回undefined
,从而导致后面的then方法的回调函数的执行结果都为undefined
。
那要想后面两个then方法的回调函数的执行结果有值,可以做如下修改:
p.then(
(value) => {
console.log('value1:', value);
+ return 'hello';
},
(reason) => {
console.log('reason1:', reason);
},
)
.then(
(value) => {
console.log('value2:', value);
+ return Promise.resolve('world');
},
(reason) => {
console.log('reason2:', reason);
},
)
.then(
(value) => {
console.log('value3:', value);
},
(reason) => {
console.log('reason3:', reason);
},
);
/* 执行结果:
value1: success
value2: hello
value3: world
/*
体会一下下面代码的输出结果:
const p = new Promise((resolve, reject) => {
reject('err');
});
p.then(
(value) => {
console.log('value1:', value);
return 'hello';
},
(reason) => {
console.log('reason1:', reason);
throw 'err2';
},
)
.then(
(value) => {
console.log('value2:', value);
return Promise.resolve('world');
},
(reason) => {
console.log('reason2:', reason);
return 1000;
},
)
.then(
(value) => {
console.log('value3:', value);
},
(reason) => {
console.log('reason3:', reason);
},
);
/* 执行结果:
reason1: err
reason2: err2
value3: 1000
*
总结:新的promise(记为promise1)的结果状态:
- 简单来说:由
then()
指定的回调函数执行的结果决定。 - 详细来讲:
- 若抛出
异常
:promise1变为rejected ,reason
的值为抛出的异常; - 若返回的是
非promise
的任意值:promise1变为resolved ,value
的值为返回的任意值; - 若返回的是另一个
新的promise
:此promise的结果就会成为promise1的结果。
- 若抛出
5. promise如何串联多个任务?
promise.then()
会返回一个新的promise,可以写成then()
的链式调用;- 通过
then()
的链式调用来串联多个同步/异步任务。
看下面这段代码:
new Promise((resolve, reject) => {
// 模拟异步任务
setTimeout(() => {
console.log('执行任务1(这是异步的)');
resolve(100);
}, 1000);
})
.then((value) => {
console.log('任务1的返回结果:', value);
console.log('执行任务2(这是同步的)');
return Promise.resolve(200);
})
.then((value) => {
console.log('任务2的返回结果:', value);
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行任务3(这是异步的)');
resolve(300);
}, 1000);
});
})
.then((value) => {
console.log('任务3的返回结果:', value);
});
/* 执行结果:
执行任务1(这是异步的)
任务1的返回结果: 100
执行任务2(这是同步的)
任务2的返回结果: 200
执行任务3(这是异步的)
任务3的返回结果: 300
*/
可以看到3个任务不论同步还是异步,都是按顺序执行的。
需要注意的是:若想在then()中继续执行异步任务
,则用promise包装,在其中调用新promise的resolve方法来讲值传递出去。
6. promise的异常穿透
- 当使用then链式调用时,可以在最后在指定失败的回调;
- 当前面任何操作出了异常,都会传到最后失败的回调中处理。
new Promise((resolve, reject) => {
reject('err');
})
.then((value) => {
console.log('value1:', value);
})
.then((value) => {
console.log('value2:', value);
})
.then((value) => {
console.log('value3:', value);
})
.catch((reason) => {
console.log('reason:', reason);
});
/* 执行结果:
reason: err
*/
上面的这段代码其实相当于:
new Promise((resolve, reject) => {
reject('err');
})
.then(
(value) => {
console.log('value1:', value);
},
(reason) => {
throw reason;
},
)
.then(
(value) => {
console.log('value2:', value);
},
(reason) => {
throw reason;
},
)
.then(
(value) => {
console.log('value3:', value);
},
(reason) => {
throw reason;
},
)
.catch((reason) => {
console.log('reason:', reason);
});
reason会逐级往下传递,直到最后的catch。
7. 如何中断promise链?
当使用promise.then链式调用时,若在中间过程中断,后面的then方法就不会再调用。
new Promise((resolve, reject) => {
reject('err');
})
.then((value) => {
console.log('value1:', value);
})
.then(
(value) => {
console.log('value2:', value);
},
(reason) => {
return new Promise(() => {});
},
)
.then((value) => {
console.log('value3:', value);
})
.catch((reason) => {
console.log('reason:', reason);
});
上面的代码最后没有任何输出。由于在第二个then方法的rejected回调中返回了一个pending
状态的promise对象,因此后续的then方法也就不会再执行了。