Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
1. Promise的状态
笼统的来讲,promise有两种状态
-
pending
状态:执行时状态,promise一旦创建,就会执行进入pending
状态 -
fulfilled
状态:promise执行结束后,状态发生了改变,就会变为fulfilled
状态
代码验证:
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 0);
})
console.log(promise); // Promise {}
setTimeout(() => {
console.log(promise); // Promise {: 1}
}, 500);
上面代码创建一个promise实例,模拟执行异步操作,同步的打印第一个promise对象,所以结果为promise为执行时(pending
)状态;当promise执行完毕后,我们创建了一个setTimeout
延时操作,延迟时间为500ms,此时promise肯定已经执行完毕了,所以再打印出promise,可以看到,此时的promise为执行完毕(fulfilled
)状态。
fulfilled也可以细分为resolved
和rejected
状态,分别表示成功和失败的状态。
2. Promise特点
promise有两大特点:
- 对象的状态不受外界影响。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
使用promise的好处
-
指定回调函数的方式更加灵活:
旧的:必须在启动异步任务前指定回调函数
promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务之后)
-
支持链式调用,可以解决回调地狱问题
回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是在嵌套的回调函数执行的条件
回调地狱缺点:不便于阅读 / 不便于处理异常
解决方案:promise链式调用
终极解决方案:
async / await
当然,promise也有缺点:
- 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
- 当处于pending状态时,无法得知目前进展到哪一个阶段。
3. Promise常用API方法
3.1 基本使用
// 1. 创建一个Promise对象
const p = new Promise((resolve, reject) => { // 执行器函数
// 2. 执行异步任务
setTimeout(() => {
const time = +new Date()
if (time % 2 === 0) {
// 3.1 如果成功了,调用resolve(value)
resolve('成功了,' + time)
} else {
// 3.2 如果失败了,调用reject(reason)
reject('失败了,' + time)
}
}, 1000);
})
p.then(value => { // 接收得到成功的value数据 onResolved
console.log(value);
}, reason => { // 接手得到失败的reason数据 onRejected
console.log(reason);
})
3.2 Promise.resolve()
函数方法,返回一个成功的promise对象
const p = Promise.resolve(1)
p.then(value => {
console.log(value); // 1
})
// 相当于
cosnt p = new Promise((resolve, reject) => {
resolve(1)
})
3.3 Promise.reject()
函数方法,返回一个失败的promise对象
const p = Promise.reject(2)
p.catch(reason => {
console.log(reason); // 2
})
// 相当于
const p = new Promise((resolve, reject) => {
reject(2)
})
3.4 Promise.all(iterable)
接收的参数为一个数组或者是一个实现iterator接口的参数,每个元素是一个promise实例
在执行过程中,如果数组中所有的promise对象执行完成后的状态都是resolved,则会回调then的成功方法;如果有一个promise对象的执行结果为rejected状态,则会回调失败的回调
成功:各个promise的成功结果将会以数组的形式回调成功函数onResoloved
失败:调用onRejected函数
成功示例
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
Promise.all([p1, p2, p3]).then(value => {
console.log(value); // [1,2,3]
}).catch(reason => {
console.log('reason', reason); // 不会执行,因为所有结果都成功了
})
失败示例
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
Promise.all([p1, p2, p3]).then(value => {
console.log('value', value); // 不会执行
}).catch(reason => {
console.log('reason', reason); // 3
})
3.5 Promise.allSettled(iterable)
接收的参数为一个数组或者是一个实现iterator接口的参数,每个元素是一个promise实例
无论参数中的promise对象成功与否,都会返回成功的回调
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.resolve(3)
const p4 = Promise.reject(4)
Promise.allSettled([p1, p2, p3, p4]).then(value => {
console.log('value', value);
// 结果 是一个数组
// 0: {status: "fulfilled", value: 1}
// 1: {status: "fulfilled", value: 2}
// 2: {status: "fulfilled", value: 3}
// 3: {status: "rejected", reason: 4}
// length: 4
}).catch(reason => {
console.log('reason', reason);
})
3.6 Promise.race(iterable)
方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
只要有一个promise对象状态改变,就会被返回
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 3000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2)
}, 1000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3)
}, 2000);
})
Promise.race([p1, p2, p3]).then(value => {
console.log('value', value);
}).catch(reason => {
console.log('reason', reason); // 由于p2用时时间最短,且是第一个状态发生改变的,所以执行它的结果
})
3.7 Promise.prototype.then(onResolved, onRejected)
then方法接收两个回调函数,第一个是promise的成功回调,第二个是promise的失败回调函数。
Promise.resolve(1)
.then(value => {
console.log('onResolved', value); // onResolved 1
}, reason => {
console.log('onRejected', reason);
})
3.8 Promise.prototype.catch(reason)
很显然,catch
就像try...catch...
中的catch
一样,能捕捉到上游发生的错误
Promise.reject(1)
.then(value => {
console.log('onResolved', value);
})
.catch(reason => {
console.log('onCatch', reason); // onCatch 1
})
如果上游的错误已经被处理了,那么catch方法就不会捕获到
Promise.reject(1)
.then(value => {
console.log('onResolved', value);
}, reason => {
console.log('onRejected', reason); // onRejected 1
})
.catch(reason => {
console.log('onCatch', reason);
})
可以看到,上游的reject(1)
被then方法中的onRejected()
回调函数捕获了,所以不会触发catch
方法
如果上游有多个异常错误,则出现第一个错误后后面就不会执行了,会被后面的onRejected()回调函数或者catch()方法捕获到
new Promise((resolve, reject) => {
resolve(2)
})
.then(value => {
console.log('onResolved1()', value); // onResolved1() 2
// 抛出的第一个错误
throw new Error('onResolved1()异常')
})
.then(value => {
// 此时此处不会走
console.log('onResolved2()', value);
}, reason => {
// 异常被处理了
console.log('onRejected2()', reason); // onRejected2() Error: onResolved1()异常
// 又抛出一个异常
throw new Error('onRejected2()异常')
})
.catch(reason => {
console.log('onCatch', reason); // onCatch Error: onRejected2()异常
})
3.9 Promise.prototype.finally()
无论如何都会执行的回调函数。无论上游是否成功或者失败,最终都会执行
成功示例
Promise.resolve(1)
.then(value => {
console.log('onResoloved', value); // onResoloved 1
})
.catch(reason => {
console.log('onCatched', reason);
})
.finally(() => {
console.log('finally1执行了'); // finally1执行了
})
失败示例
Promise.reject(1)
.then(value => {
console.log('onResoloved', value);
})
.catch(reason => {
console.log('onCatched', reason); // onCatched 1
})
.finally(() => {
console.log('finally1执行了'); // finally1执行了
})
多个finally也都会依次执行
Promise.resolve(1)
.then(value => {
console.log('onResoloved', value); // onResoloved 1
})
.catch(reason => {
console.log('onCatched', reason);
})
.finally(() => {
console.log('finally1执行了'); // finally1执行了
})
.finally(() => {
console.log('finally2执行了'); // finally2执行了
})
4 promise.then()返回的新promise的结果状态有什么决定?
简单表达:由then()指定的回调函数执行的结果决定
详细表达:
- 如果抛出异常,新promise变为rejected,reason为抛出的异常
- 如果返回的是非promise的任意值,新promise变为resolved,value为返回的值
- 如果返回的是另一个新promise,此promise的结果就会成为新promise的结果
4.1 promise成功后then接收后继续使用then
new Promise((resolve, reject) => {
resolve(1)
})
.then(value => {
console.log('onResolved1()', value); // onResolved1() 1
}, reason => {
console.log('onRejected1()', reason);
})
.then(value => {
console.log('onResolved2()', value); // onResolved2() undefined
}, reason => {
console.log('onRejected2()', reason);
})
- 可以看到,promise中使用resolve(1)改变promise状态,并触发第一个then的onResolved()函数,所以此时打印出
onResolved1() 1
- 第一个then的onResolved()函数没有返回值,或者说它的返回值为
undefined
(默认返回值)。根据promise的特性,promise的then方法返回值是一个新的promise对象。暂且隐式命名为promise2,promise2发现没有异常错误,则会把第一个then中onResolved()的返回值resolve出去,即resolve(undefined)
- 第二个then接收的是promise2的回调函数,由于上一步promise2
resolve(undefined)
了,所以会触发then方法的onResolved()回调,由于value是undefined,所以打印结果为onResolved2() undefined
4.2 promise失败后then接收后继续使用then
new Promise((resolve, reject) => {
reject(2)
})
.then(value => {
console.log('onResolved1()', value);
}, reason => {
console.log('onRejected1()', reason); // onRejected1() 2
})
.then(value => {
console.log('onResolved2()', value); // onResolved2() undefined
}, reason => {
console.log('onRejected2()', reason);
})
原理同上
4.3 promise成功后有返回值,返回值为一个普通值
new Promise((resolve, reject) => {
resolve(1)
})
.then(value => {
console.log('onResolved1()', value); // onResolved1() 1
return 2
}, reason => {
console.log('onRejected1()', reason);
})
.then(value => {
console.log('onResolved2()', value); // onResolved2() 2
}, reason => {
console.log('onRejected2()', reason);
})
原理同上
4.4 promise成功后有返回值,返回值是一个promise对象
new Promise((resolve, reject) => {
resolve(1)
})
.then(value => {
console.log('onResolved1()', value); // onResolved1() 1
return Promise.reject(2)
}, reason => {
console.log('onRejected1()', reason);
})
.then(value => {
console.log('onResolved2()', value);
}, reason => {
console.log('onRejected2()', reason); // onRejected2() 2
})
如果then中onResolved()和onRejectd()回调函数返回值是一个promise对象,则下一个then函数则会以此promise对象为基础进行回调,就不会以then的返回新的promise对象为基础了。
Promise实战
光说不练只能是纸上谈兵,下面使用promise结合ajax请求实现一个获取接口数据的方法
/**
* 请求封装
*/
function get(url, resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open('GET', url)
xhr.send()
xhr.onload = function () {
if (this.status === 200) {
resolve(this.response)
} else {
reject(this.response)
}
}
xhr.onerror = function () {
reject(this.status)
}
}
// 使用promise
new Promise((resolve, reject) => {
// 获取数据
get('https://api.apiopen.top/getJoke?page=1&count=2&type=video', resolve, reject)
})
.then(value => {
console.log('获取数据成功', value);
})
.catch(reason => {
console.log('获取数据失败', reason);
})
Promise常见面试题汇总
【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理)