前言
昨天面试去xiaoman, 提到了promise;
面对不断深入发问, 我脑子一片空白, 然后就想到了白云山;
我想怎么思考其他问题的时候没有这么思维活跃;
Promise 状态的不可逆性
Promise状态的一旦变成resolved或rejected时,Promise的状态和值就固定下来了,不论你后续再怎么调用resolve或reject方法,都不能改变它的状态和值。
var p1 = new Promise(function(resolve, reject){
resolve("success1");
resolve("success2");
});
var p2 = new Promise(function(resolve, reject){
resolve("success");
reject("reject");
});
p1.then(function(value){
console.log(value);
});
p2.then(function(value){
console.log(value);
});
// "success1"
// "success"
resolve或reject后面的代码会不会执行
当然会
then的第二个参数和catch
catch
相当于then(null,function(err){ /*处理错误*/})
的写法
// 写法一
new Promise((resolve)=>{
foo.bar()
}).then(res=>{
console.log(res)
},err=>{
console.log(err)
})
// ReferenceError: foo is not defined
// 写法二
new Promise((resolve)=>{
foo.bar()
}).then(res=>{
console.log(res)
kk()
}).catch(err=>{
console.log(err)
})
// ReferenceError: foo is not defined
上面代码中,catch
要好于then(null,err=>{})
写法, catch可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)
。因此,建议总是使用catch
方法,而不使用then
方法的第二个参数。如下:
new Promise((resolve)=>{
resolve(1)
}).then(res=>{
console.log(res)
kk()
}).catch(err=>{
console.log(err)
})
// ReferenceError: kk is not defined
then/catch的链式调用
首先, 在new Promise
中需要执行resolve
或者reject
改变状态,否则不会执行then
方法, 像下面then
就不会执行:
var p = new Promise(function(resolve, reject){
return 1
});
p.then(function(value){
console.log(value);
return value*2;
}).then(function(value){
console.log(value);
})
// Promise {<pending>}
在状态改变后, then
方法返回一个新的Promise
对象,因此可以通过链式调用then
方法.
catch
也可以链式调用, 因为catch
相当于then(null,err=>{})
函数的返回值将被用作创建then
返回的Promise
对象, 有如下情况:
return
一个同步的值(当没有返回时,默认返回undefined
),then
方法将返回一个resolved
状态的Promise
对象,Promise
对象的值就是这个返回值。return
一个Promise
,then
方法将根据这个Promise
的状态和值创建一个新的Promise
对象返回。throw
一个同步异常,then
方法将返回一个rejected
状态的Promise
, 值是该异常。
var p = new Promise(function(resolve, reject){
resolve(1);
});
p.then(function(value){ //第一个then
console.log(value);
return value*2;
}).then(function(value){ //第二个then
console.log(value);
}).then(function(value){ //第三个then
console.log(value);
return Promise.resolve('resolve');
}).then(function(value){ //第四个then
console.log(value);
return Promise.reject('reject');
}).then(function(value){ //第五个then
console.log('resolve: '+ value);
}, function(err){
console.log('reject: ' + err);
})
1
2
undefined
"resolve"
"reject: reject"
再写一个return
返回promise
的例子:
var a = new Promise(resolve=>{
setTimeout(()=>{
resolve('5s later')
},5000)
})
var p = new Promise(function(resolve, reject){
resolve(1);
});
p.then(function(value){ //第一个then
console.log(1)
return a
}).then(function(value){ //第二个then
console.log(value);
})
// 1
// Promise {<pending>}
// 5s later
Promise.resolve()
Promise.resolve()
(注意,是Promise.resolve()
,而不是new Promise
中的resolve
)可以接收一个值或者是一个Promise
对象作为参数。
当参数是普通值时,它返回一个resolved
状态的Promise
对象;
当参数是Promise
对象时,对象的值就是这个参数;
下面的例子可以看到p1,p2是相等的
let p1 = Promise.resolve(1)
let p2 = Promise.resolve(p1)
p1 === p2
// true
then拿到数据发现不合适, 怎么触发catch
throw new Error()
抛出错误. 或者 Promise.reject()
resolve和reject
当resolve
的参数是一个Promise
对象时,resolve
会"拆箱"获取这个Promise
对象的状态和值,但这个过程是异步的。
即最后的状态是根据传入的promise
对象的状态确定的;
如下: 执行的是resolve(a)
, 但是a
5秒后返回的状态是rejected
,所以会执行rejected
回调
var a = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('error')
},5000)
})
var p = new Promise(function(resolve, reject){
var b = resolve(a)
console.log(a === b)
});
p.then(function(value){
console.log('success')
},function(err){
console.log(err)
return err
})
// false
// Promise {<pending>}
// error
但是, reject
方法不同, reject
方法不存在拆箱等待, 而是直接将传入的promise
对象传入下个阶段, 如下
var a = new Promise((resolve,reject)=>{
setTimeout(() => {
reject('error after 5s')
},5000)
})
var p = new Promise(function(resolve, reject){
reject(a)
})
p.then(function(value){
console.log('success')
}).catch(err1=>{ // 第一个 catch
console.log('fail: ' + err1)
return err1
}).catch(err2=>{ // 第二个 catch
console.log(err2)
return 1
}).then(res=>{
console.log(res)
})
// fail: [object Promise]
// Promise {<pending>}
// error after 5s
// 1
可以看到, p
中reject
了a
, 直接把a
传到了第一个 catch
, 第一个catch中, 打印出来参数, 就是promise
对象a
.
在 第一个catch
中return err1
, 等待5秒后a
变成rejected
状态, 触发第二个 catch
, 捕获到结果error after 5s
,
最后catch
可以继续链式调用, 返回1
, 相当于resolve(1)
, 最后then
打印出1
.
promise中出现异常, promise外的后面的代码会执行吗
会, Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。
new Promise(function(resolve, reject){
resolve(x+1);
});
console.log(333)
// 333
// Uncaught (in promise) ReferenceError: x is not defined
promise.all
let a = new Promise(resolve=>{setTimeout(()=>{resolve(111)},3000)})
let b = new Promise(resolve=>{setTimeout(()=>{resolve(222)},1000)})
let c = new Promise(resolve=>{setTimeout(()=>{resolve(333)},100)})
Promise.all([b,a,c]).then(res=>{
console.log(res)
})
// 结果数组也是顺序的
// [222, 111, 333]
let a = new Promise(resolve=>{setTimeout(()=>{console.log('haha');resolve(111);},3000)})
let b = new Promise((resolve,reject)=>{setTimeout(()=>{reject(222)},1000)})
let c = new Promise(resolve=>{setTimeout(()=>{resolve(333)},100)})
Promise.all([b,a,c]).then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)
})
只要有一个错误,就会catch该错误
// 222
// haha
Promise如何按顺序执行
有一个数组,数组元素是返回Promise对象的函数,怎样顺序执行数组中的函数,即在前一个数组中的Promise resolve之后才执行下一个函数?
…
- 先生成数据, 下面代码执行完,生成10个函数组成的数组, 均返回promise对象
function createPromises(id) {
return function () {
return new Promise(resolve => {
setTimeout(() => {
console.log(id)
resolve(id)
}, 1000)
})
}
}
let list = []
for(let i = 0; i < 10; i++) {
list.push(createPromises(i))
}
// (10) [ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ]
- 递归法
// 递归调用
function promise_queue(list, i) {
if (i >= 0 && i < list.length) {
list[i]().then(() => {
promise_queue(list, i + 1)
})
}
}
promise_queue(list, 0)
- await
async function promise_queue(list) {
let index = 0
while (index >= 0 && index < list.length) {
await list[index]()
index++
}
}
promise_queue(list)
- Promise.resolve()
初始状态是Promise.resolve()
,then
执行第一个, 并把下一个作为then
的success
函数(因为then
返回一个promise
的话,promise
状态改变后, 才会执行下一个then
)
function promise_queue(list) {
var sequence = Promise.resolve()
list.forEach((item) => {
sequence = sequence.then(item)
})
return sequence
}
- reduce
list.reduce((pre,next)=>pre.then(next),Promise.resolve());
promise实现每隔一段时间执行一次函数, 执行n次 , 不用await
每隔interval毫秒, 执行一次fn函数, 执行time次:
function interval(fn, interval, time){
let f = () => new Promise(resolve=>{
setTimeout(()=>{
fn()
resolve()
},interval)
})
let a = Promise.resolve()
for(let i = 0; i< time; i++){
a = a.then(f)
}
}