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(() => {})