Promise
之前我知道Promise 怎么用,却不知道为什么要用Promise
有的人可以会想,执行异步操作我用async/await 就行了,为什么还要学promise?
因为 带 async 关键字的函数,它使得你的函数的返回值必定是 promise 对象
所以async的原理实现就是用的promise,不学promise可能不会对你的使用有太大影响,但是如果你知道原理,我觉得你可以会用的更加顺手。
Why
前端操作中,经常要有异步操作,比如在等待服务端返回response的时候,异步操作显得格外重要。
看下面的这段代码,并猜测一下运行结果是什么
const value = (() => {
console.log(1);
let res = 0;
setTimeout(() => {
console.log(2);
res = 4
}, 0)
console.log(3);
return res;
})()
console.log(value);
如果你的答案是下面中的其中一种:
1 2 3 4
1 3 2 4
那么你就错了.
如果你是第一种,那你就全错了,这时候我建议你去看一下下面这篇文章window.setTimeout - Web API 接口参考 | MDN (mozilla.org),了解一下setTimeout后再阅读下面的部分
上面那段代码的运行结果是这样的
1
3
0
2
你可能会很疑惑?为什么是0?我不是将res设置为4了吗?
一种解释是:console.log(value)
比res=4
先执行,所以res还没有更新
但是如果你的代码是这么写的
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log(2);
resolve(4);
}, 0);
console.log(3);
})
promise.then(
(value) => {
console.log(value);
},
(reason) => {
console.log("error");
})
这时候运行结果就如你所想的那样
1
3
2
4
用另外一句话说:你的代码运行顺序如你所想的那样
所以通俗一点讲,promise的作用:让你的代码运行顺序和你想的一样
规范一点讲:
- 异步操作更加灵活,可以在操作后指定回调函数
- 支持链式调用,解决回调地狱
What
1. 一种实现异步操作的解决方案
2. 封装了异步操作并且可以获得其结果
3. 状态有三种:pending,resolved,rejected
- pending:等待状态,没有成功,也没有失败
- resolved:成功状态
- rejected:失败状态
注意点
1. promise有且只有两种状态变化
- pending–>resolved
- pending–>rejected
2. promise改变状态的方式
- resolve():pending->resolved
- return: pending->resolved
- reject():pending->rejected
- throw:pending->rejected
3. 指定多个成功/失败回调函数时,都会执行相应状态变化的函数
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log(2);
resolve(4);
}, 0);
console.log(3);
})
promise.then((value) => {
console.log(value);
})
promise.then((value) => {
console.log(value+1);
})
运行结果是1 3 2 4 5
也就是说,then可以指定两次
4. then()返回的结果由执行的回调函数决定
new Promise((resolve, reject)=>{
resolve(1);
}).then( // 1
value=>{
console.log("onResolved1()",value);
throw 2;
},
reason=>{
console.log("onRejected1()",reason);
}
).then( // 2
value=>{
console.log("onResolved2()",value);
throw 2;
},
reason=>{
console.log("onRejected2()",reason);
}
)
运行结果:
onResolved1() 1
onRejected2() 2
这里出现了两个then连着用,这就是then的链式调用,第二个then的结果由第一个then所执行的回调函数执行结果决定
5. then()指定回调函数,是同步执行,执行回调函数是异步执行,这点要分清楚
6. 在then的回调函数中执行异步任务,需要return new promise
现在有一个需求,我要再then中执行settimeout,也就是执行异步任务,我该怎么做?
你可能会这么写
new Promise((resolve, reject) => {
resolve(1);
}).then(
value => {
console.log("onResolved1()", value);
let res = 0;
setTimeout(() => {
console.log("task 异步");
res = 3;
}, 1000);
return res;
},
reason => {
console.log("onRejected1()", reason);
}
).then(
value => {
console.log("onResolved2()", value);
},
reason => {
console.log("onRejected2()", reason);
}
)
但是这次代码的执行顺序又和你想的不一样了,上面代码的执行结果是这样的
onResolved1() 1
onResolved2() 0
task 异步
而你期望的执行结果应该是这样
onResolved1() 1
task 异步
onResolved2() 3
又出现了之前的问题?
上一次我们用promise解决了问题,那这次我们还能不能用promise解决问题?
可以可以可以
new Promise((resolve, reject) => {
resolve(1);
}).then(
value => {
console.log("onResolved1()", value);
// 再return一个promise
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log("task 异步");
resolve(3);
},1000);
})
},
reason => {
console.log("onRejected1()", reason);
}
).then(
value => {
console.log("onResolved2()", value);
},
reason => {
console.log("onRejected2()", reason);
}
)
7. rejected的传递
有时候你会出现这样一种需求:我得在rejecte前面执行很多个resolve,这其中可能会出现错误,我希望错误可以传递下来。
new Promise((resolve, reject) => {
reject(1);
}).then(
value => {
console.log("onResolved1()", value);
return 2;
},
// 可以不写,默认会加
).then(
value => {
console.log("onResolved2()", value);
return 3
},
reason => { throw reason }
).then(
value => {
console.log("onResolved3()", value);
},
// 没有写函数体时,默认加上return
reason => Promise.reject(reason)
).catch(reason => {
console.log('onRejected1()',reason);
})
你可以看到你需要的运行结果
onRejected1() 1
8. 中断promise链
有时候,你想要中断你写的那个很长的then链,怎么办?
返回一个pending状态的promise!!!
new Promise((resolve, reject) => {
reject(1);
}).then(
value => {
console.log("onResolved1()", value);
return 2;
},
// 可以不写,默认会加
).then(
value => {
console.log("onResolved2()", value);
return 3
},
reason => { throw reason }
).then(
value => {
console.log("onResolved3()", value);
},
// 没有写函数体时,默认加上return
reason => Promise.reject(reason)
).then(
value => {
console.log("onResolved3()", value);
},
// 没有写函数体时,默认加上return
reason => {return new Promise(()=>{})} // 返回pending状态的promise
).catch(reason => {
console.log('onRejected1()',reason);
})