Promise
英文翻译过来是诺言、承诺的意思
顾名思义,Promise
的潜在含义是:
promise
代表的是还没发生的事而在Javascript
中的Promise
代表的意义是类似的,对于异步操作(网络请求、文件I/O等)这些事情在完成之前无法确定其状态,在完成的时候要么成功要么失败,Promise
会在完成的时候通知你。
上面是Promise
的定义,Promise
是一个表示异步事件处理完成的对象,我们看看传统的异步操作是怎样的:
function successCallback(result) {
console.log('Result is ready: ', result);
}
function failureCallback(error) {
console.log('Error on async request: ', error);
}
requestData(options, sucessCallback, failureCallback)
在传统的异步操作下,整个接口的定义实现方式,没有一个清晰的定义,不同的人去写,接口的定义方式不一样。
function successCallback(result) {
console.log('Result is ready: ', result);
}
function failureCallback(error) {
console.log('Error on async request: ', error);
}
requestData(options).then(successCallback).catch(failureCallback)
在Promise
中,抽象出了异步操作成功(then)的接口和失败的接口(catch/reject),并且支持链式调用,在传统的callback
的实现中,如果下一个异步操作依赖于上一个操作,很容易写出callback hell
的这种代码,对于代码的阅读和维护带来麻烦。
Promise
对象对象是一个代理对象,被代理的值表示一个异步操作,在创建的时候,对于最终异步操作的返回结果是未知的,Promise
允许为异步操作的成功和失败分别绑定相应的处理方法,让异步方法可以像同步方法一样返回值,但并不是立即返回最终的执行结果,而是一个代理了未来出现的结果的Promise
对象,Promise
有以下几种状态:
pending:
初始状态,创建promise
之后,执行还没结束的时候fulfilled:
异步操作成功rejected:
异步操作失败
如上图,在promise
创建后,所处的状态为pending
状态,执行成功或失败的这个过程叫settled
,settled
之后,promise
由pending
状态转换到fulfilled
或rejected
状态,并执行相应的回调。
我们首先来看看Promise
提供的方法:
常见的方式是通过Promise
的构造函数得到Promise的实例:
function executor(resolve, reject) {
const r = asyncWork();
r.on('success', (s) => {
resolve(s);
})
r.on('error', (e) => {
reject(e);
})
}
const p = Promise(executor)
如上Promise
的构造函数接收一个executor
函数为参数,这个executor
一般来说是一个异步操作,在生成实例的时候会被执行,在执行成功的时候调用resolve
,失败的时候调用reject
。
Promise.prototype.then(onFulfilled?, onRejected?)
,在promise resolve
的时候onFulfilled
会被调用,reject
的时候onReject
会被调用,这两个参数是可选的,如果你只想对异常进行处理的话,可以使用promise.then(undefined, onRejected)
Promise.prototype.catch(onRejected)
,catch
方法会在promise reject
的时候被调用。Promise.prototype.finally(onFinally)
,向当前promise
添加一个回调函数,无论当前promise
的状态是完成还是失败都会被调用Promise.resolve(value)
,返回一个由给定value
决定的promise
对象;如果这个value
可以是一个thenable
的对象(带有then
方法的对象),最终返回的promise
对象的状态由then
方法执行决定;否则的话,返回的promise
对象状态为fulfilled
,并且将该值传给对应的then
方法,如果你不知道一个值是否是Promise
对象,使用Promise.resolve(value)
来返回一个Promise
对象,这样就能将该value
以Promise
对象的形式使用。Promise.reject(reason)
,返回一个执行状态为rejected
的Promise
对象,并将错误信息给到对应的处理函数Promise.race(iterable)
,当iterable
中的任意一个子promise
成功或者失败后,父promise
会使用这个子promise
的结果,传给父promise
绑定的回调上Promise.all(iterable)
,这个方法返回一个promise
对象,只有iterable
中所有的promise
执行成功的时候才会触发成功,一旦由任何一个执行失败都会触发要返回这个promise
额失败,最终iterable
的返回结果和iterable
的顺序一致。promise
是支持链式调用的:
Promise.resolve({
then: (resolve, reject) => {
resolve(1);
}
})
.then((r) => {
console.log(r)
})
.then(() => {
console.log(2)
})
.finally(() => {
console.log(3)
})
.then(() => {
console.log(4)
})
// use `catch`
new Promise((resolve, reject) => {
console.log('Init')
resolve()
})
.then(() => {
throw new Error('error') // {A}
})
.then(() => { // {B}
console.log('123');
})
.catch(() => {
console.log('Error was caught!')
})
.then(() => {
console.log('End')
})
// use reject
new Promise((resolve, reject) => {
console.log('Init')
resolve()
})
.then(() => {
throw new Error('error')
})
.then(() => {
console.log('End')
}, () => {
console.log('Error was caught!')
})
promise
在处理错误的时候,可以通过Promise.ptototype.catch
或者是注册的reject
方法来处理错误,在{A}
行处,抛出了错误,这里promise
的状态会变为rejected
,会调用对应的回调函数,在{A}
到.catch
之间的promise
链会被打断。
function delay(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms);
})
}
我们在使用setTimeout
的时候,setTimeout
执行的函数如果执行发生了错误,并且在函数的实现中并没有做Error Handling
,函数执行发生错误的时机我们并不知道,如果使用上面基于promise
包裹之后的timeout
,我们是可以明确知道错误发生的时机的。
promise
创建之后,会等待异步操作的执行,如果异步操作的时间很长的话,这个promise
一直处于 pending
状态,对于用户来说,页面会一直停留在loading
的状态,显然,用户体验并不好,因此,对于这种情况,我们需要提供可以在超时之后,取消promsie
的机制:
function delayPromise(ms) {
return delay(ms).then(() => {
console.error('Operation is timeout!');
})
}
function timeoutPromise = (asyncFn, ms) {
return Promise.race([
new Promise(asyncFn),
delayPromise(ms)
]);
}
如上我们基于Promise.race
实现了超时promise
。
兴趣遍地都是,坚持和持之以恒才是稀缺的