Promise
1.Promise的含义
Promise是异步编程的解决方案,比回调函数和事件更强大和合理。
Promise,一个容器,保存着某个未来才会结束的事件(通常是异步操作)的结果。
语法上,Promise就是一个对象,可以获取异步操作结束后的消息。
Promise的两个特点:
- 对象的状态不受外界影响。Promise代表一个异步操作,具有三种状态:pending(进行中)、fulfilled(已完成)、rejected(已失败)。只有异步操作的结果可以决定装态,其他任何操作都不行。
- 状态改变后就不会再变了,且任何时候都可以得到这个结果。Promise对象状态的改变有两种可能:从pending到fulfilled和从pending到rejected。只要状态发生,就凝固了,一直保持这个结果。即,resolved(已定型)。
Promise的缺点:
无法取消,一旦新建,立即执行。
若不设置回调函数,Promise内部抛出的错误无法反应到外部。
当处于pending状态时,无法得知目前进展到哪个阶段(刚开始还是即将完成。)
2.基本用法
Promise对象是一个构造函数,用来生成Promise实例。
let promise = new Promise((resolve,reject) => { // some code if(/*异步操作成功*/true){ resolve('操作成功') return } reject('操作失败') })
Promise构造函数接受一个函数作为参数,该函数的参数是resolve和reject两个函数,这两个函数由JavaScript引擎提供。
resolve函数:将Promise对象的状态从未完成变为成功(pending–>resolved),并将成功之后要传递的结果作为参数抛出去。
reject函数:将Promise对象的状态从未完成变为失败(pending–>rejected),并将异步操作失败后报出的错误作为参数传递出去。
实例生成后,用then方法指定resolved状态和rejected状态的回调函数。
// 接上述promise promise.then((value) => { console.log(value); },(error) => { console.log(error); }) // 第一个函数在Promise变为resolved时调用,第二个函数在Promise变为rejected时调用,第二个函数可以省略。两个函数都接受Promise对象抛出的值作为参数。
Promise新建后立即执行。
new Promise((resolve, reject) => { console.log("object"); resolve('resolve') }).then((result) => { console.log(result); }) console.log("Hi!"); // object // Hi // resolve
reject函数的参数通常是Error对象的实例,表示抛出的错误。resolve函数的参数除了正常的值外,还可能是另一个Promise实例。
let p1 = new Promise((resolve, reject) => { // ... }); let p2 = new Promise((resolve, reject) => { resolve(p1) }); const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) const p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error)) //上面代码中,p1是一个 Promise,3 秒之后变为rejected。 //p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。 //所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。
调用resolve和reject后,并不会终结Promise参数函数的执行。
new Promise((resolve, reject) => { resolve(1) console.log(2); }).then((result) => { console.log(result); }) // 2 // 1
一般而言,调用resolve或reject之后,Promise的任务就结束了,后续的操作应该放在then方法里面。所以,最好加上return语句。
new Promise((resolve, reject) => { return resolve('value') console.log("object"); // 这一句不会执行 });
Promise.prototype.then()
Promise的实例具有then方法,作用是为Promise实例添加状态改变时的回调函数。
then方法第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
then方法返回新的Promise实例,因此可以采用链式写法。
new Promise((resolve, reject) => { return resolve('promise1') }).then(res => { console.log(res); return 'promise2'; }) .then(res => { console.log(res) }) // 第一个then中回调函数返回的结果,会作为参数传入第二个then中的回调函数。
使用链式的then,可以指定一组按照次序调用的回调函数。若前一个回调函数返回的是Promise对象,则后一个回调函数则会等待Promise对象的状态发生变化后,才会被调用。
new Promise((resolve, reject) => { if (true) { return resolve('my value') } reject('my error') }).then((params) => { return new Promise((resolve, reject) => { if (true) { return resolve('output value') } reject('error') }) }).then((result) => { console.log(result); }).catch((error) => { console.log(erro) }) // 第二个then方法指定的回调函数会等待新Promise对象的状态变化,若变为resolved,则调用第一个回调函数,若变为rejected,则调用第二个回调函数。
Promise.prototype.catch()
这个方法是,.then(null,rejecttion)或者.then(undefined,rejection)的别名,用于指定发生错误时的回调函数。
new Promise((resolve, reject) => { // dosomething }).then((params) => { // dosomething }).catch((error) => { console.log(error) }) // 第一个Promise中reject的错误和then回调函数中抛出的错误都会被catch()方法所捕获。
Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获。即,错误总会被下一个catch()语句所捕获。
new Promise((resolve, reject) => { // dosomething }).then((params) => { // dosomething }).then((params) => { // dosomething }).catch((params) => { // dosomething }) // 三个Promise对象中任意一个所产生的的错误都会被catch()方法所捕获。
一般而言,不建议在then()方法里定义rejected状态的回调函数(即第二个参数),而总是使用catch()方法捕获错误。
// bad new Promise((resolve, reject) => { }).then((res) => { },(err) => { }) // good new Promise((resolve, reject) => { }).then((result) => { }).catch((err) => { });
没有使用catch()方法指定错误处理的回调函数,Promise对象抛出的错误就不会传递到外层,即不会有任何反应。
new Promise((resolve, reject) => { resolve(x+33) }).then((result) => { console.log(result); }) let timer = setTimeout(() => { console.log("object"); }, 2000); // 上述代码在2s后还是会打印object,不会因为报错而停止执行,即Promise会吃掉错误。
catch()方法返回的还是一个Promise对象,后面可以继续接then()方法。
如果Promise对象没有抛出错误,则会跳过catch()方法。
new Promise((resolve, reject) => { resolve(s+333) }).catch((err) => { console.log(err) }).then((res) => { console.log('end') }) // ReferenceError: s is not defined // end
catch()方法之中,也能再抛出错误。
someAsyncThing().then(function() { return someOtherAsyncThing(); }).catch(function(error) { console.log('oh no', error); // 下面一行会报错,因为y没有声明 y + 2; }).catch(function(error) { console.log('carry on', error); }); // oh no [ReferenceError: x is not defined] // carry on [ReferenceError: y is not defined] // 第一个catch()方法中的错误被第二个catch()方法所捕获。
Promise.prototype.finally()
finally()方法是不管最后Promise对象的状态如何,都会执行的操作。finally()方法的回调函数不接受任何参数,即表明finally方法里面的操作是与状态无关的,与Promise的执行结果没有关系。
new Promise((resolve, reject) => { }).then((result) => { }).catch((err) => { }).finally(()=> { })
finally()方法本质上是then()方法的特例。
new Promise((resolve, reject) => { // dosomething }).finally(() => { // code }) // 等同于 new Promise((resolve, reject) => { // dosomething }).then((res) => { // code return res },(err) => { // code throw err })
finally()方法的实现。
Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); };
从上述实现中可以看出,finally()方法返回原来的值(就是Promise抛出的值??)。
// resolve 的值是 undefined Promise.resolve(2).then(() => {}, () => {}) // resolve 的值是 2 Promise.resolve(2).finally(() => {}) // reject 的值是 undefined Promise.reject(3).then(() => {}, () => {}) // reject 的值是 3 Promise.reject(3).finally(() => {})