最近由于对 promise 的状态依赖理解有误差,致使在开发过程当中花费了3个小时的时间才发现错误。感受浪费时间了,因此结合标准及实践结果对 promise 的状态依赖作了一个总结。javascript
问题代码大体是这样的:java
// 假设promise为请求错误回调
let promise = new Promise((resolve, reject) => {
reject('400');
});
// 统一的响应拦截处理
promise.then((res) => {
console.log('promise injector resolved', res)
}, (err) => {
console.log('promise injector rejected', err)
})
// 请求调用处的业务响应处理
.then((res) => {
console.log('promise resolved', res)
}, (err) => {
console.log('promise rejected', err)
})
复制代码
上面代码中表现的场景是,错误请求通过请求响应拦截器的统一处理后,业务逻辑自己再根据请求状态来进行一些其余的处理。原本按照我对promise的理解,这样是没有问题的。实际上这里的问题是,业务中的请求响应处理永远只会走成功回调,而不会走失败回调。由于在进行统一的响应拦截处理的时候,就已经丢失了 promise 的状态了。promise
promise共有是三种状态:异步
这三种状态是 Promises/A+ 中描述状态的术语函数
当咱们建立一个 promise 时,它的状态是 Pending,而后随着异步任务的执行,它的状态必定会变成 Fulfilled 和 Rejected 中的一种,且它的状态不会再发生任何变化(这一点很重要,后面捋清楚 promise.then() 的返回值就靠这个特性了)。ui
先来一代码:spa
let promise = Promise.resolve('test');
let thenPromise = promise.then( res =>{
console.log(res)
});
console.log(promise === thenPromise);
console.log(thenPromise);
复制代码
运行结果以下: 3d
从运行结果中咱们不可贵出两点:code
promise.then() 返回的是一个新的 Promise 的实例对象,而 promise 是可以能表示状态的,这样就能够造成一条状态的依赖链。也就能将多个任务之间的依赖及执行顺序表示出来了,从而将异步任务的回调嵌套转化成为一条扁平的链条。cdn
promise chain 说白了就是个 promise 的调用链,代码形式大体以下:
var promise1 = Promise.reject('test');
promise1.then(function(res) {
console.log('fulfilled1', res)
}, function(err) {
console.log('rejected1', err)
return err;
}).then(function(res) {
console.log('fulfilled2', res)
}, function(err) {
console.log('rejected2', err)
})
复制代码
这样的链式调用方式,很好的将多个存在依赖关系的异步任务,将难看回调嵌套转化成了一条更易于理解的扁平链条。从而解决了所谓回调地狱的问题。
当咱们建立了一个 promise 以后,就可使用 promise.then() 来注册,Fulfilled 和 Rejected 状态对应的执行函数了。相似下面这样:
var promise = new Promise((resolve, reject)=>{
resolve('ok');
});
promise.then((res)=>{
console.log('fulfilled', res)
}, (err)=>{
console.log('rejected', err)
})
复制代码
promise.then() 注册的回调函数能够返回不一样的值,分为如下两种:
promise.then() 回调函数的返回值决定了其返回的 promise,大体分为如下两种情形:
注意: 这里的回调函数指的是 promise.then() 注册的回调函数,且是指的是被调用的函数,与是成功回调仍是失败回调无关
当 promise.then() 注册的回调函数返回除promise之外的值时,返回的值会被当作 promise.then() 返回的新的 promise 使用 .then() 注册的回调函数的传入值。
当回调函数未返回值时,考虑下面代码:
let promise = Promise.resolve();
promise.then(()=>{
console.log('promise resolved')
}, ()=>{
console.log('promise rejected')
})
.then((value)=>{
console.log('promise resolved1')
console.log(value)
}, (err)=>{
console.log('promise rejected1')
console.log(err)
})
// promise resolved
// promise resolved1
// undefined
let promise1 = Promise.reject();
promise1.then(()=>{
console.log('promise1 resolved')
}, ()=>{
console.log('promise1 rejected')
})
.then((value)=>{
console.log('promise1 resolved1')
console.log(value)
}, (err)=>{
console.log('promise1 rejected1')
console.log(err)
})
// promise1 rejected
// promise1 resolved1
// undefined
复制代码
promise 注册的回调运行了成功回调,然后返回了一个成功状态的promise,这个返回的 promise 注册的回调运行了成功回调,回调函数的实参是 undefined;promise1 注册的回调运行了失败回调,然后一样返回了一个成功状态的 promise ,这个返回的 promise 注册的回调运行了成功回调,回调函数的实参是 undefined
js 函数执行后默认返回值为undefined
再来看看返回其余值的状况:
let promise = Promise.resolve();
promise.then(()=>{
console.log('promise resolved')
return 'value'
}, ()=>{
console.log('promise rejected')
return 'err'
})
.then((value)=>{
console.log('promise resolved1')
console.log(value)
}, (err)=>{
console.log('promise rejected1')
console.log(err)
})
// promise resolved
// promise resolved1
// value
let promise1 = Promise.reject();
promise1.then(()=>{
console.log('promise1 resolved')
return 'value'
}, ()=>{
console.log('promise1 rejected')
return 'err'
})
.then((value)=>{
console.log('promise1 resolved1')
console.log(value)
}, (err)=>{
console.log('promise1 rejected1')
console.log(err)
})
// promise1 rejected
// promise1 resolved1
// err
复制代码
promise.then() 注册的回调函数,运行的是成功回调,且返回了字符串 “value”,promise.then() 函数返回了一个成功状态的 promise, 返回的 promise 注册的回调函数运行了成功回调,回调函数的实参是字符串“value”;promise1.then() 注册的回调函数,运行的是失败回调,且返回了字符串 “err”,promise1.then() 函数返回了一个成功状态的 promise, 返回的 promise 注册的回调函数运行了成功回调,回调函数的实参是字符串“err”
从上面两段示例代码及其运行结果,不可贵出以下结论:
当 promise.then() 注册的被运行的回调函数返回一个 Promise 实例的时候,回调返回的 Promise 实例的最终的状态会决定 promise.then() 函数返回的这个新的 promise 的状态。且promise.then() 函数返回的 promise 注册的回调函数的实参是由 Promise 实例对象 resolve 或 reject 传递的参数决定的。
考虑以下代码:
let promiseValue = Promise.reject('promiseValue err');
let promise = Promise.resolve();
let promiseThen = promise.then(()=>{
console.log('promise resolved');
return promiseValue;
})
promiseThen.then((res)=>{
console.log('promise resolved1');
console.log(res)
}, (err)=>{
console.log('promise rejected1');
console.log(err);
})
// promise resolved
// promise rejected1
// promiseValue err
console.log(promiseThen === promiseValue)
// false
复制代码
promise 执行成功状态回调,回调函数返回失败状态的 Promise 实例对象 promiseValue,且其 rejecte 传入的值是字符串 “promiseValue err”。由于 promiseValue 的状态是 rejected 因此 promise.then() 返回的新的 Promise 实例(promiseThen === promiseValue 的结果是 false 证实promise.then()返回的)promiseThen 的状态是 rejected,promiseThen 注册的失败回调被调用,打印的实参为字符串“promiseValue err”。
promise.then() 注册的被执行的回调函数返回 Promise 实例对象时:
正是由于 promise.then() 返回的是一个新的 Promise 实例,且这个实例能够向后传递上一个依赖 promise 的异步任务的执行状态和其要传递的数据。这样的链式调用机制,解决了存在依赖关系的异步任务只能在回调函数中不断嵌套的最后致使代码难以维护的问题。