Promise
Promise是异步编程的一种解决方案,它比传统的解决方案--回调函数和事件--更合理且更加强大,它最早是由社区提出并实现,后面ES6将其写进了语言标准,统一了用法,也提供了Promise。
特点
- 对象的状态不受外界影响(3中状态)
(1) Pending状态(进行中)
(2) Fulfilled状态(已成功)
(3) Rejected状态(已失败)
- 一旦状态改变就不会再发生变化(两种状态改变:成功或失败)
(1) Pending(进行中) -> Fulfilled(已成功)
(2) Pending(进行中) -> Rejected(已失败)
用法
创建Promise实例
const promise = new Promise(function(resolve, reject) {
if (/*异步操作成功*/) {
resolve(value)
} else {
reject(error)
}
})
- Promise构造函数接受一个函数作为参数,该函数接受两个参数,分别是resolve和reject,它们是两个函数,是由javascript引擎提供,不用自己部署。
- resolve作用是将Promise对象状态由‘未完成’变为‘成功’,也就是Pending -> Fulfilled,在异步操作成功时调用,并将异步操作的结果作为参数传递出去,而rejected函数则是将Promise对象状态由‘未完成’变为‘失败’,也就是Pending -> Rejected,在异步操作失败时调用,并将异步操作的结果作为参数传递出去。
Promise的方法
- then,这个方法是定义在原型对象Promise.prototype上的,它的作用是为Promise实例添加状态改变时的回调函数。它返回的是一个新的Promise实例,注意,不是原来的那个Promise实例,因此可以采用链式写法,即then方法后面再调用一个then方法。
Promise实例生成后,可用then方法分别指定两种状态回调函数,then方法可用接受两个回调函数作为参数:
(1) Promise对象状态改为Resolved时调用(必须)
(2) Promise对象状态改为Rejected时调用(可选)
(3) 基本用法:
function sleep(ms) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, ms);
})
}
sleep(500).then( ()=> console.log("finished"));
这段代码定义了一个函数sleep,调用后,等待了指定参数(500)毫秒后执行then中的函数。值得注意的是,Promise新建后就会立即执行。
catch方法。这个方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
上面代码中,getJSON方法返回一个Promise对象,如果该对象状态变为resolved,则会调用then方法指定的回调函数,如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误,另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。
p.then((val) => console.log('fulfilled:', val))
.catch((err) => console.log('rejected', err));
// 等同于
p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log("rejected:", err));
下面一个例子
const promise = new Promise(function(resolve, reject) {
throw new Error('test');
});
promise.catch(function(error) {
console.log(error);
});
// Error: test
上面代码中,promise抛出一个错误,就被catch方法指定的回调函数捕获,注意,上面的写法与下面的两种写法时等价的。
// 写法一
const promise = new Promise(function(resolve, reject) {
try {
throw new Error('test');
} catch(e) {
reject(e);
}
});
promise.catch(function(error) {
console.log(error);
});
// 写法二
const promise = new Promise(function(resolve, reject) {
reject(new Error('test'));
});
promise.catch(function(error) {
console.log(error);
});
如果Promise状态已经变成resolved,再抛出错误是无效的。
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test');
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
// ok
上面代码中,Promise在resolve语句后面,再抛出错误,不会被捕获,等于没有抛出,因为Promise的状态一旦改变,就永久的保持该状态,不会再发生变化。
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止,也就是说,错误总是会被下一个catch语句捕获。
getJSON('/post/1.json').then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前面三个Promise产生的错误
});
上面代码中,一共有三个Promise对象:一个是由getJSON产生,另外两个是由then产生,它们之中有任何一个抛出错误,都会被最后一个catch捕获。
一般来说,不要在then方法里面定义Reject状态的回调函数(即then的第二个参数)就使用catch方法
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
上面的代码中,第二种写法要好于第一种写法,因为第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch方法,而不使用then方法的第二个参数。