Promise的理解
Promise是异步编程的解决方案,比传统的解决方案——回调函数和事件——更合理更强大。
Promise是一个构造函数,它会接收一个函数作为参数,并且返回一个Promise实例,可以通过它获取异步操作的信息;它也有提供统一的API,各种异步操作都可以通过同样的方式进行处理。
Promise有三个状态:
pending
:进行中resolved
(fulfilled):成功rejected
:失败
当把一件事交给Promise时,它就是起初的状态pending,当任务完成了状态也就变成了resolved, 而任务失败了状态就会变成rejected,所以Promise只有两个过程:
pending--->resolved
pending--->rejected
注意:一旦状态进行了改变就不会再改变。
Promise的优点:
- 指定回调函数方式会更加灵活
- 支持链式调用,可以解决回调地狱的问题
- 对象的状态不会受外界影响,只有异步的操作才能决定当前是哪一种状态,其他任何操作都无法改变这个状态。
- 一旦状态改变就不会再变,并且任何时候都可以得到这个结果。
Promise的缺点:
- 无法取消Promise,一旦新建它就会立刻执行,中途没有办法取消
- 如果不设置回调函数的话,Promise内部抛出的错误,不会反应到外部去
- 当处于刚开始的pending状态时,无法得知目前进展到哪一个阶段了
如何使用Promise
Promise构造函数
Promise(executor){}
executor
函数:同步执行(resolve,reject)=>{}
resolve
函数:成功时调用的函数resolve(value)
reject
函数:内部定义失败时调用的函数reject(reason)
Promise会接收一个函数作为参数,而这个函数又会有两个参数,分别是resolve
和reject
。
const p = new Promise((resolve, reject) => {
//do something
if (/*异步操作成功*/) {
resolve()
} else {
reject()
}
})
Promise.prototype.then()
p.then(onResolved, onRejected)
onResolved
函数:成功的回调函数(value) => {}
onRejected
函数:失败的回调函数(reason) => {}
then()方法是用来调用之前创建好的Promise对象.
then()
方法会接收两个回调函数作为参数,第一个回调函数是Promise对象的状态变为resolved时
调用,第二个回调函数是Promise对象的状态变为resolved
时调用;其中第二个参数可以省略。
这两个回调函数中还可以接收一个参数,而这个参数的值就是在创建Promise对象时,调用resolve或者reject方法所带的值
then()
方法会返回一个新的Promise实例(并不是之前的那个Promise实例),所以可以采取链式写法,即then()方法后面再调用一个then()方法。
// 无传参
const p = new Promise((resolve, reject) => {
// do something
if (/*异步操作成功*/) {
resolve()
} else {
reject()
}
});
p.then(() => {
// 成功后执行的操作
}, () => {
// 失败后执行的操作
})
// 有传参
const p = new Promise((resolve, reject) => {
// do something
if (/*异步操作成功*/) {
resolve(/*参数*/)
} else {
reject(/*参数*/)
}
})
p.then((value) => { /*value是上面成功后resolve传递过来的参数*/
// 成功后执行的操作
}, (reason) => { /*reason是上面失败后reject传递过来的参数*/
// 失败后执行的操作
})
console.log(p) //返回的是一个Promise对象
例:
//成功
const pOne = new Promise((resolve, reject) => {
resolve('这是成功了')
})
pOne.then((value) => {
console.log(value) // 这是成功了
}, (reason) => {
console.log(reason) //上面的状态是成功,所以此处无输出
})
console.log(pOne); // Promise { <resolved>: "这是成功了" }
//失败
const pTwo = new Promise((resolve, reject) => {
reject('这是失败了')
})
pTwo.then((value) => {
console.log(value) // 同理上面的状态是失败,所以此处无输出
}, (reason) => {
console.log(reason); //这是失败了
})
console.log(pTwo); // Promise {<rejected>: '这是失败了'}
Promise.prototype.catch()
p.catch(onRejected)
onRejected
函数:失败的回调函数(reason) => {}
Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名(是then()的语法糖),用于指定发生错误时的回调函数
const pOne = new Promise((resolve, reject) => {
resolve('成功的数据') // resolve()函数
})
const pTwo = new Promise((resolve, reject) => {
reject('失败的数据') //reject()函数
})
pOne.then((value) => { // onResolved()函数
console.log(value) // 成功的数据
}).catch((reason) => { // onRejected()函数
console.log(reason) // 失败的数据
})
pTwo.then((value) => { // onResolved()函数
console.log(value) // 成功的数据
}).catch((reason) => { // onRejected()函数
console.log(reason) // 失败的数据
})
Promise.resolve()
Promise.resolve(value)
value
:将被 Promise 对象解析的参数,也可以是一个成功或失败的 Promise 对象
Promise.resolve()
的返回值也是一个promise对象,可以对返回值进行.then()
方法的调用
- 如果传入的参数是一个
非promise
,则返回的就是成功的promise对象,结果是参数值;
例:
const p = Promise.resolve(521);
console.log(p); // Promise {<fulfilled>: 521}
- 如果传入的参数是一个
promise
对象,则该promise对象返回的结果就决定了resolve的结果;
例:
//传入的promise的结果是一个成功的
const pOne = Promise.resolve(new Promise((resolve, reject) => {
resolve('OK') //成功
}));
console.log(pOne); //Promise {<fulfilled>: 'OK'}
//传入的promise的结果是一个失败的
const pTwo = Promise.resolve(new Promise((resolve, reject) => {
reject('ERROR') //失败
}));
console.log(pTwo); //Promise {<rejected>: 'ERROR'}
Promise.reject()
Promise.resolve(reason)
reason
:失败的原因
它将返回一个失败的promise
对象;
- 如果传入的参数是
非promise
,则返回的就是一个失败的promise对象,结果 就是该参数值
//传入的参数是一个非promise
const p = Promise.reject(521)
console.log(p); //Promise {<rejected>: 521}
- 如果传入的参数是一个
promise
,则返回的是一个失败的promise对象,而结果是传入的这个promise返回的结果;
// 传入的参数是一个成功的promise
const pOne = Promise.reject(new Promise((resolve, reject) => {
resolve('OK') //成功
}));
console.log(pOne); //Promise {<rejected>: Promise}
// 传入的对象是一个失败的promise
const pTwo = Promise.reject(new Promise((resolve, reject) => {
reject('ERROR') //失败
}));
console.log(pTwo); //Promise {<rejected>: Promise}
Promise.all()
Promise.all(iterable)
iterable
:包含 n 个 promise 的可迭代对象,如 Array 或 String
它将会返回一个新的promise
,但是只有全部的promise都成功了,返回的新的promise的结果才是成功,否则就是失败。
- 若传入的
promise
的结果都是成功,则返回的一个成功的promise,结果就是含有传入的全部promise的结果值的一个数组。 - 若传入的
promise
的结果有一个或更多的失败,则返回的就是一个失败的promise,结果就是传入的promise中第一个失败的结果值。
//全成功
let pOne = new Promise((resolve, reject) => {
resolve('OK');
})
let pTwo = Promise.resolve('Success');
let pThree = Promise.resolve('Oh Yeah');
const resultOne = Promise.all([pOne, pTwo, pThree]);
console.log(resultOne);
//有一个失败
let pFour = new Promise((resolve, reject) => {
resolve('OK');
})
let pFive = Promise.reject('Oh No 1');
let pSix = Promise.reject('Oh No 2');
const resultTwo = Promise.all([pFour, pFive, pSix]);
console.log(resultTwo);
Promise.race()
Promise.race(iterable)
iterable
:包含 n 个 promise 的可迭代对象,如 Array 或 String
它会返回一个新的promise
,结果是第一个完成的promise
的结果状态以及值(谁先完成输出谁,不管是成功还是失败)
// pOne 延迟完成 全部都为成功
let pOne = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
})
let pTwo = Promise.resolve('Success');
let pThree = Promise.resolve('Oh Yeah');
//调用
const resultOne = Promise.race([pOne, pTwo, pThree]);
console.log(resultOne);
// 谁先完成就输出谁(不管是成功还是失败)
// pFour延迟完成,pFive为失败
const pFour = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
const pFive = Promise.reject(2)
const pSix = Promise.resolve(3)
const resultTwo = Promise.race([pFour, pFive, pSix])
Promise的几个关键问题
1、如何改变promise的状态?
(1)resolve(value)
:如果当前是 pending
就会变为 resolved
(2)reject(reason)
:如果当前是 pending
就会变为 rejected
(3)抛出异常:如果当前是 pending
就会变为 rejected
// 成功
const pOne = new Promise((resolve, reject) => {
resolve(1) // promise变为resolved成功状态
})
pOne.then(
value => { console.log('value', value) },
reason => { console.log('reason', reason) }
)
console.log('pOne', pOne);
// 失败
const pTwo = new Promise((resolve, reject) => {
reject(2) // promise变为rejected失败状态
})
pTwo.then(
value => { console.log('value', value) },
reason => { console.log('reason', reason) }
)
console.log('pTwo', pTwo);
// 抛出错误
const pThree = new Promise((resolve, reject) => {
throw new Error('出错了') // 抛出异常,promise变为rejected失败状态,reason为抛出的error
})
pThree.then(
value => { console.log('value', value) },
reason => { console.log('reason', reason) }
)
console.log('pThree', pThree);
2、一个 promise 指定多个成功/失败回调函数,都会调用吗?
当promise
的状态改变为相对应的状态时就会调用
// 状态为成功时
const pOne = new Promise((resolve, reject) => {
resolve('成功')
})
pOne.then(
value => { console.log('value1', value) },
reason => { console.log('reason1', reason) }
)
pOne.then(
value => { console.log('value2', value) },
reason => { console.log('reason2', reason) }
)
console.log(pOne);
// 状态为失败时
const pTwo = new Promise((resolve, reject) => {
reject('失败')
})
pTwo.then(
value => { console.log('value1', value) },
reason => { console.log('reason1', reason) }
)
pTwo.then(
value => { console.log('value2', value) },
reason => { console.log('reason2', reason) }
)
console.log(pTwo);
3、改变 promise 状态和指定回调函数谁先谁后?
都有可能,常规是先指定回调再改变状态,但也可以先改变状态再指定回调。
- 如何先改变状态再指定回调?
(1)在执行器中直接调用resolve()
/reject()
(2)延迟更长时间再调用then()
例:
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功') //这个是改变状态
}, 1000) //有定时器(异步)就先指定回调再改变状态
})
p.then( //这个是调用回调
value => { console.log('value1', value) },
reason => { console.log('reason1', reason) }
)
- 什么时候才能得到数据?
(1)如果先指定的回调,那当状态发生改变时,回调函数就会调用得到数据;
(2)如果先改变的状态,那当指定回调时,回调函数就会调用得到数据。
//先指定了回调函数,保存了当前指定的回调函数,再改变了状态(同时指定数据),然后异步执行之前保存的回调函数。
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1) // 改变状态
}, 1000)
}).then( // 指定回调函数 (先指定)
value => {},
reason =>{}
)
//这个是先改变了状态(同时指定了数据),然后指定回调函数,并且直接异步执行回调函数。
new Promise((resolve, reject) => {
resolve(1) // 改变状态
}).then( // 指定回调函数
value => {},
reason =>{}
)
4、promise.then() 返回的新 promise 的结果状态由什么决定?
(1)简单的来说:它的结果是由then()
所指定的回调函数执行的结果来决定的;
const p = new Promise((resolve, reject) => {
resolve('成功')
})
const result = p.then(
value => { console.log('value1', value) },
reason => { console.log('reason1', reason) }
)
console.log(result);
(2)详细的来说:
- 如果
抛出异常
,则promise.then()
返回的新promise
的状态变为rejected
(失败),结果值为抛出错误的值
const p = new Promise((resolve, reject) => {
resolve('成功')
})
const result = p.then(value => {
throw '失败' //抛出异常
}, reason => {
console.log('reasons', reason)
}
)
console.log(result);
- 如果返回的是一个
非promise
,则promise.then()
返回的新promise
的状态为resolve
(成功),结果值为该非promise值
const p = new Promise((resolve, reject) => {
resolve('成功')
})
const result = p.then(value => {
return 521 //返回一个非promise
}, reason => {
console.log('reasons', reason)
}
)
console.log(result);
- 如果返回的是一个
promise
对象,则promise.then()
返回的新 promise
的状态就是上面这个返回的promise
对象的状态,结果值也是上面这个返回的promise对象的值;
const p = new Promise((resolve, reject) => {
resolve('成功')
})
const resultOne = p.then(value => {
return new Promise((resolve, reject) => { //返回一个promise
resolve('success')
})
}, reason => {
console.log('reasons', reason)
})
const resultTwo = p.then(value => {
return new Promise((resolve, reject) => { //返回一个promise
reject('error')
})
}, reason => {
console.log('reasons', reason)
})
console.log(resultOne);
console.log(resultTwo);
例:
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log('onResolved1()', value)
//return 2 // onResolved2() 2
//return Promise.resolve(3) // onResolved2() 3
//return Promise.reject(4) // onRejected2() 4
//throw 5 // onRejected2() 5
},
reason => {
console.log('onRejected1()', reason)
}
).then(
value => {
console.log('onResolved2()', value)
},
reason => {
console.log('onRejected2()', reason)
}
)
// onResolved1() 1
// onResolved2() undefined
// Promise {<fulfilled>: undefined}
// 对应输出如上所示
因为返回的是一个
promise
对象,其也可以用then()
方法,而使用then()
方法会根据该promise
对象的状态去调用resolve()
或者reject()
,而结果值就是该promise
对象的结果值。
5、promise 如何串联多个操作任务?
(1)promise
的then()
会返回一个新的promise
,可以接着使用then()
,形成链式调用
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
});
p.then(value => {
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => {
console.log(value); // success
}).then(value => {
console.log(value); // undefined
})
(2) 通过使用then()
的链式调用来串联多个同步/异步任务
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行任务1(异步)')
resolve(1)
}, 1000)
}).then(
value => {
console.log('任务1的结果', value)
console.log('执行任务2(同步)')
return 2 // 同步任务直接return返回结果
}
).then(
value => {
console.log('任务2的结果', value)
return new Promise((resolve, reject) => { // 异步任务需要包裹在Promise对象中
setTimeout(() => {
console.log('执行任务3(异步)')
resolve(3)
}, 1000)
})
}
).then(
value => {
console.log('任务3的结果', value)
}
)
// 执行任务1(异步)
// 任务1的结果 1
// 执行任务2(同步)
// 任务2的结果 2
// 执行任务3(异步)
// 任务3的结果 3
6、Promise 异常穿透?
(1)当使用 promise
的 then()
链式调用时,可以在最后指定失败的回调
//这样的写法,当失败时直接在最后接收失败的结果
new Promise((resolve, reject) => {
//resolve(1) //依次输出1,2,3
reject(1) // onRejected1() 1
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(
reason => {
console.log('onRejected1()', reason)
}
)
(2)前面任何操作出了异常,都会传到最后失败的回调中处理
//这样的写法,是将失败的结果一层一层的传递到最后
new Promise((resolve, reject) => {
//resolve(1) //依次输出1,2,3
reject(1) // onRejected1() 1
}).then(
value => {
console.log('onResolved1()', value)
return 2
},
reason => {throw reason} // 抛出失败的结果reason
).then(
value => {
console.log('onResolved2()', value)
return 3
},
reason => {throw reason} // 抛出失败的结果reason
).then(
value => {
console.log('onResolved3()', value)
},
reason => {throw reason} // 抛出失败的结果reason
).catch(
reason => {
console.log('onRejected1()', reason)
}
)
7、中断 promise 链?
当使用
promise
的then()
链式调用时,在中间中断,不在调用后面的回调函数
办法:在回调函数中返回一个pending
状态的promise
对象
const p = new Promise((resolve, reject) => {
resolve('成功')
})
// 中断Promise链
p.then((value) => {
console.log('value1', value);
return new Promise(() => { }) //直接返回一个pending状态的Promise对象,也可以写在catch中
}).then((value) => { //后续的then不会执行
console.log('value2', value);
}).then((value) => {
console.log('value3', value);
}).catch((reason) => {
console.log('reason', reason);
})
console.log(p);
由于,
promise
的状态决定了后面then()
中的结果,当返回一个pending
状态的promise
时,后续的then()
也就不会去执行。