专业英语:
单词 | 释义 |
---|---|
promise | n. 许诺,允诺;希望 |
pending | adj. 未决定的;行将发生的 |
resolve | vi. 解决;决心;分解 |
reject | vt. 拒绝;排斥;抵制;丢弃 |
Promise概述
- Promise主要是用来处理异步任务的。
- 在JS中,promise的含义是“期约”。
- ECMAScript 6中全局环境中新增了引用类型Promise,可以通过new操作符来实例化。创建新期约时需要传入执行器(executor)函数作为参数。
const promise = new Promise(function(resolve, reject) {
// code
})
- 期约对象有两个内部属性,其中
[[PromiseState]]
表示当前状态,[[PromiseResult]]
表示执行结果。
-
期约是一个有状态的对象,可能处于如下三种状态之一:
- 待定(pending):简单的说,在异步操作中,这就表示操作还未结束;
- 兑现(fulfilled/resolved):在异步操作中,这表示操作已经结束,且结果为成功;
- 拒绝(rejected):在异步操作中,这表示操作已经结束,但结果为失败;
-
待定状态可以转化为兑现状态或者拒绝状态,但这个过程是不可逆的,转换过程由用户传入的执行器控制:
new Promise(function(resolve, reject) {
// do something
resolve('hello world'); // 主动调用resolve,将状态转化为兑现(此时它接收一个参数作为执行结果)
})
new Promise(function(resolve, reject) {
// do something
reject('hello world'); // 主动调用reject,将状态转化为拒绝(此时它接收一个参数作为执行结果)
})
- 注意转换是不可逆的,也即转换为兑现或者拒绝后状态就无法再改变:
new Promise(function(resolve, reject) {
// do something
resolve(); // 主动调用resolve,将状态转化为兑现
reject(); // 因为状态已经变成了兑现,所以这里的操作会被静默拒绝(忽略)。
})
- 状态的转换只能由执行器函数决定,如果我们不主动调用
resolve
或者reject
,那么状态将一直保持为pending
。
Promise.prototype.then()
-
Promise.prototype.then()
是为了给期约实例添加处理程序。then方法最多接收两个函数作为参数:(传给then的任何非函数参数都会被忽略)
new Promise(() => {}).then(onResolved, onRejected);
-
onResolved
也即期约转化为兑现时的回调函数。onRejected
是期约转化为拒绝的回调函数。这两个函数都会被传入一个值,那就是期约的执行结果。 - 当执行器主动调用
resolve
转化期约状态后,onResolved就会作为回调函数被调用(注意这里resolve不等于onResolved,他们是不同的)。
new Promise(function(resolve, reject) {
// do something
resolve('hello world'); // 主动调用resolve,将状态转化为兑现
console.log('测试点1');
}).then(result => {
console.log(result);
})
/* 控制台输出:
测试点1
hello wolrd
*/
- 当执行器主动调用
reject
转化期约状态后,onRejected
就会作为回调函数被调用:
new Promise(function(resolve, reject) {
// do something
reject('hello world'); // 主动调用reject,将状态转化为兑现
console.log('测试点1');
}).then(null, result => {
console.log(result);
})
/* 控制台输出:
测试点1
hello wolrd
*/
-
then()
函数不需要Promise实例化后立即调用,可以在任何时候调用then传递处理程序,而如果在你调用then传递参数时,Promise状态已经发生了转化,有了执行结果,那么你传递的处理程序会立即被执行(这很重要);另外,你也可以多次调用then:
const promise = new Promise(function(resolve, reject) {
// do something
resolve('hello world'); // 主动调用reject,将状态转化为兑现
})
setTimeout(function () {
promise.then(result => console.log('测试点1', result))
promise.then(result => console.log('测试点2', result))
}, 1000)
/*
测试点1 hello world
测试点2 hello world
*/
- 此外
then()
函数也是有返回值的,它会返回一个新的Promise实例,该实例具有如下特征:- 当原实例状态为
pending
时,这个新实例的状态也会保持为pending
; - 当原实例状态发生了转化,但我们没有设置对应状态的处理程序(如果状态为resolved,但我们只设置了onRejected,那么也算是未设置处理程序),那么这个新的实例就会使用
Promise.resolve()
(后面会详细说明)基于原有实例的执行结果重构当前状态。 - 在第二种情况下,如果存在对应状态的处理程序,那么新实例就会继续保持
pending
状态,等待处理程序执行完毕并返回结果,然后使用Promise.resolve()
基于该返回值构建新实例。(实际上我也没太弄懂参考书上的描述,结果有点怪异,但从测试结果来看大概是这样的) - 如果原有的Promise实例在执行过程中发送了错误(如状态转化为
reject
但是我们没有设置onRejected
处理函数),那么新实例的状态就会变为rejected
,执行结果就是原本实例的执行结果。
- 当原实例状态为
console.log(
new Promise((resolve, reject) => resolve('resolve')).then(),
new Promise((resolve, reject) => resolve('resolve')).then(result => 'then ' + result),
new Promise((resolve, reject) => resolve('resolve')).then(undefined, result => 'then ' + result)
);
console.log(
new Promise((resolve, reject) => reject('reject')).then(),
new Promise((resolve, reject) => reject('reject')).then(result => 'then ' + result),
new Promise((resolve, reject) => reject('reject')).then(undefined, result => 'then ' + result)
);
-
Promise
的链式调用与传值现象:利用then的返回值特性,我们可以对Promise对象进行链式调用,并且这其中存在有趣的传值现象- 因为
then()
方法总是会返回一个新的Promise实例对象,而每个实例对象都可以调用then()
方法,于是我们可以写成promise.then().then().then()......
; - 根据
then()
返回值的特点我们可以知道,如果我们没在then()
中添加对应状态的处理程序,那么新生成的Promise对象就会继承原来的Promise实例对象的执行结果,这样就会形成层层向后传递的现象,直到某个then()
中添加了对应状态的处理程序。
- 因为
new Promise(resolve => resolve('hello')).then().then(result => console.log(result));
// hello
new Promise((resolve, reject) => reject('hello')).then().then(undefined, result => console.log(result));
// hello
new Promise((resolve, reject) => reject('hello'))
.then(result => console.log('test1', result))
.then(undefined, result => console.log('test2', result));
// test2 hello
new Promise((resolve, reject) => resolve('hello'))
.then(result => console.log('test1', result))
.then(result => console.log('test2', result), result => console.log('test3', result));
// test1 hello
// test2 undefined
Promise.resolve()
- 该方法是Promise对象上的方法,而不是原型上的方法,因此不会被继承。
-
resolve()
方法接收一个参数,然后返回一个Promise实例对象。根据传入参数的不同,返回的Promise实例也会有所区别。
- 传入的参数不是对象类型:返回的实例对象状态为兑现(fulfilled/resolved),且执行结果就是我们传入的参数
- 传入的参数是一个普通对象:结果与第一种情况一样。
console.log(Promise.resolve('hello world'),
Promise.resolve(undefined),
Promise.resolve(null),
Promise.resolve({test: 'hello'}));
- 传入的参数是Promise实例对象:原封不动的返回这个对象。
- 参数是一个含有
then()
方法的对象:将其中的then()
方法作为执行器函数包装成Promise实例对象后返回。
const obj = {
then(resolve, reject) {
console.log('then函数被调用');
resolve('hello world');
}
}
Promise.resolve(obj).then(result => console.log('测试点', result));
/*
then函数被调用
测试点 hello world
*/
延迟执行 + 链式调用
- 前不久面试了某大厂的前端实习岗位,其中有一道代码题是让我写出一个
obj.print('start').sleep(3000).print('测试点1').sleep(3000).print('end')
这样的链式调用的(不能使用队列的方式),且可以延迟执行的对象,当时试了半天也没写出来,于是痛并思痛,决定好好研究一下Promise
,现在终于可以轻松搞定了,代码如下:
const timeOutPrint = {
promise: Promise.resolve(),
sleep(time) {
this.promise = this.promise.then(() => new Promise(resolve => setTimeout(resolve, time)))
return this;
},
print(msg) {
this.promise = this.promise.then(() => console.log(msg));
return this;
}
}
timeOutPrint.print('start').sleep(3000).sleep(3000).print('测试点1').sleep(3000).print('end');