Promise 是异步编程的一种解决方案,比传统的回调更加合理而其人强大。javascript
Promise,简单来讲,是一个容器,里面存放着某个将来才会结束的事件的结果。Promise是一个对象,用来获取异步操做的消息。java
Promise有如下两个特色:编程
1 对象状态不受外界影响。json
有三种状态:Pending(进行中),Fulfilled(已成功),Rejected(已失败)。只有异步操做的结果能够决定当前是哪种状态,任何操做都没法改变这个状态。数组
2 一旦改变,不会再变,任什么时候候均可以获得这个结果。promise
Promise对象的状态改变只有两种可能:从Pending 到 Fulfilled 或者 从Pending 到 Rejected。只要这两种状况发生,状态就会凝固,不会再变,一直保持这个结果。这时就称为 Resolved(已定型)。就算改变已经发生,再对Promise添加回调函数,也会获得这个结果。 这与事件(event)彻底不一样,事件的特色就是:若是错过了,再去监听是得不到结果的。app
Promise 缺点:异步
1 没法取消。一旦创建就会当即执行,没法中途取消。async
2 若是不设置回调函数,Promise内部报错不会抛出到外面。异步编程
3 当处于Pending状态,没法得知目前进展到哪一阶段(是刚刚开始,仍是即将完成?)
var promise = new Promise((resolve,reject) => {
// ...
if(/* .....*/) {
resolve(value)
} else {
reject(error)
}
})
复制代码
resolve做用是将Promise状态由 未完成编程成功,在异步操做成功时调用,并将异步操做结果做为参数传播出去。
reject函数做用是,将Promise状态从未完成变为失败,在异步操做失败时调用,并将异步操做失败报出的错误传递出去。
Promise 实例生成后,能够调用then,指定 resolved 和 rejected 状态的回调函数。
promise.then(function(val){
// success
}, function(error) {
// failure
})
复制代码
then方法接受两个回调函数做为参数。第一个是 Promise 对象的状态变为Resolved时调用,第二个回调是Promise变为Rejected时调用。第二个函数时可选的。
function timeout(ms){
return new Promise((resolve,reject) => {
setTimeout(resolve,ms,'done')
})
}
timeout(100).then((value) => {
console.log(value)
})
复制代码
Promise 新建好以后就会当即执行。
let promise = new Promise(function(resolve,reject) {
console.log('Promise');
resolve();
})
promise.then(function(){
console.log('Resolved')
})
console.log('Hi')
// Promise
// Hi
// Resolves
复制代码
异步加载图片:
function loadImgAsync(url) {
return new Promise(function(resolve,reject) {
var img = new Image()
img.onload = function(){
resolve(img)
}
img.onerror = function(){
reject(new Error('Could not load img at ' + url))
}
img.src = url
})
}
复制代码
promise 实现 AJAX
var getJSON = function (url) {
var promise = new Promise((resolve,reject)=> {
var client = new XMLHttpRequest()
client.open('GET',url)
client.onreadystatechange = handle
client.responseType = 'json'
client.setRequestHeader("Accept","application/json")
client.send()
function handler(){
if(this.readyState!==4){
return
}
if(this.status == 200){
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
})
return promise;
}
getJSON("/post.json").then((json)=>{
console.log('Contents ' + json)
}, function(error){
console.log(error)
})
复制代码
var p1 = new Promise(function (resolve,reject){
setTimeout(()=>{
reject(new Error('fail'),3000)
})
})
var p2 = new Promise(function(resolve,reject){
setTimeout(()=>{
resolve(p1)
},1000)
})
p2
.then(rsult => console.log(result))
.catch(error => console.log(error))
// Error: fail
复制代码
通常来讲,调用resolve 或者 reject 之后,Promise的使命就已经完成了,后面的操做应该放到 then ,而不该该直接写到 resolve 或者 reject 后面,因此最好在它们以前 添加 return 语句,这样就不会产生意外。
new Promise((resolve,reject) => {
return resolve(1);
// 后面不会执行
console.log(2)
})
复制代码
Promise实例具备 then 方法,做用是 为 Promise 实例添加状态改变时的回调函数。
then 方法 返回一个新的 Promise 实例(不是原来那个Promise),所以 可使用 链式写法,then 后面 再 添加 另一个 then。
getJSON("/posts.json").then(function(json){
return json.post
}).then(function(post){
// ...
})
复制代码
链式调用的 then 能够指定 一组按照次序调用的回调函数。 前一个回调函数可能返回的仍是一个Promise,后一个回调函数就会等待该Promise 对象的状态发生变化,在被调用。
getJSON("/post/1.json").then(function(post){
return getJSON(post.commentUrl)
}).then(function funcA(comments) {
console.log("resolved")
},function funcB(error){
console.log(error)
})
复制代码
这个方法 是 .then(null, rejection) 的别名,指定发生错误时的回调。
getJSON('/posts.json').then(function(posts){
// ...
}).catch(function(error){
console.log("error " + error )
})
复制代码
Promise 在 resolve 语句以后 抛出错误,并不会被捕获,等于没有抛出。由于Promise状态一旦发生改变,就会永久保存这个状态,不会改变了。
Promise 对象错误具备“冒泡”性质,会一直向后传递,直到捕获到为止,错误总会被下一个catch捕获
通常不要在 then 方法中定义 Rejected 状态的回调函数,而应老是使用catch方法。
var someAsyncThing = function(){
return new Promise((resolve,reject)=>{
// 报错 x 未声明
resolve(x + 2)
})
}
someAsyncThing().then(()=>{
return someotherAsyncThing()
}).catch(error => {
console.log('oh no' , error)
// 报错 y 没有声明
y + 2
}).then(()=>{
console.log('carry on')
})
// oh no [ReferenceError: x is not defined ]
复制代码
catch方法抛出一个错误,后面若是没有catch,致使这个错误不会被捕获,也不会传递到外层。
能够用 第二个 catch 方法捕获 前一个catch 方法抛出的错误。
someAsyncThing().then(()=>{
return someotherAsyncThing()
}).catch(error => {
console.log('oh no' , error)
// 报错 y 没有声明
y + 2
}).catch(error=>{
console.log('carry on ', error)
})
复制代码
将多个 Promise 实例 包装成一个新的 Promise实例。
var p = Promise.all([p1, p2, p3])
p 状态 由 p1,p2,p3 决定,分两种状况。
1 只有p1,p2,p3 状态都变成 fulfilled ,p 的状态 才会变成 fulfilled,此时 p1,p2,p3 返回值组成一个数组,传递给 p 的回调。
2 只要 p1,p2,p3 有一个被Rejected,p 状态也会变成 Rejected,此时,第一个被 Rejected 的实例返回值会传递给p的回调函数。
var promises = [2,3,5,7,11,13].map(function(id) {
return getJSON('/post/'+id+'.json')
})
promise.all(promises).then(posts=>{
//...
}).catch(reason=>{
//...
})
复制代码
另外一个例子
const databasePromise = connectDatabase()
const bookPromise = databasePromise.then(findAllBooks)
const userPromise = databasePromise.then(getCurrentUser)
Promise
.all([bookPromise,userPromise])
.then(([books,user])=>{
//...
})
复制代码
若是做为参数的Promise自身定义了catch方法,那么被rejected大时并不会触发 Promise.all 的 catch 方法
var p = new Promise.race([p1,p2,p3])
复制代码
只要 p1,p2,p3中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise 实例 返回值就是传递给p 的回调函数。
const p = Promise.race([
fetch('/xxx'),
new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(new Error('time out'))
},5000)
})
])
p.then(res=>{
console.log(res)
})
.catch(error => {
console.log(error)
})
复制代码
不管Promise对象的回调链以then方法仍是catch结尾,最后一个方法抛出的错误均可能没法捕捉到(Promise内部的错误不会冒泡的全局)所以,提供一个 done 方法,老是处于回调链尾端,保证抛出任何可能的错误。
asyncFunc()
.then()
.catch()
.then()
.done()
复制代码
实现
Promise.prototype.done = function(onFulilled, onRejected){
this.then(onFulilled, onRejected)
.catch(function(resason){
setTimeout(() => {throw reason},0)
})
}
复制代码
finally方法用于指定无论Promise对象最后状态如何都会执行的操做。与 done 最大的区别在于 他接受一个不普通的回调函数做为参数,该函数无论怎样都必须执行。
实现
Promise.prototype.finally = function(callback){
let P = this.constructotr
return this.then(
value => P.resolve(callback()).then(()=>value),
reason => P.resolve(callback()).then(()=> {throw reason})
)
}
复制代码