-
Promise 的含义
- Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了
Promise
对象。 - 所谓promise ,简单来说就是一个容器,里面保存着未来才会结束的事件(通常是一个异步的操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
- Promise 对象 有两个特点:
- 对象的状态不受外界影响。Promise 对象代表着一个异步操作,有三种状态:pending(等待态)、fulfilled(已完成、rejected(以失败)。只有异步操作结果可以决定是哪一种状态。任何其他操作都无法改变这个状态,这也是promise (承诺)的由来。
- promise 的状态一旦改变就不会再变,任何时候都可以得到这个结果。promise 的状态的改变,只有两种情况:①从pending => fulfilled 或 ②从pending => rejected。
- 有了
Promise
对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise
对象提供统一的接口,使得控制异步操作更加容易。 Promise
也有一些缺点。首先,无法取消Promise
,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。第三,当处于pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
- Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了
-
基本用法
- ES6 规定,
Promise
对象是一个构造函数,用来生成Promise
实例。 - 下面代码创造了一个
Promise
实例。-
const promsime = new Prosime(function(resolve, reject){
// todo ... 做一些事情
if (/* 如果异步操作成功 */){
resolve(data);
} else {
reject(error);
}
});
-
Promise
构造函数接受一个函数 executor,这个函数接受两个参数 一个 resolve 和 reject。这两个参数由javaScript提供,不用自己部署。Promise
实例生成以后,可以用then
方法分别指定resolved
状态和rejected
状态的回调函数。-
promsime.then(data => { // resolved
// sunccess
}, err => { // rejected
// failure
})
-
- then 方法接受两个回调函数作为参数, 一个是成功的时候回调, 一个是失败的时候回调. 第二个参数是可选的.
- eg:一个promise的简单例子
-
function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(resolve, ms, 'done');});}
timeout(100).then((value) => {console.log(value);});
-
- promise 新建后会立即执行
-
let promise = new Promise((resolve, reject) => { console.log('Promise'); resolve(); }); promise.then(function() { console.log('resolved.'); }); console.log('Hi!');
// Promise // Hi! // resolved
-
- 下面是异步加载图片的例子。
-
function loadImageAsync(url) { return new Promise(function(resolve, reject) { const image = new Image(); image.onload = function() { resolve(image); }; image.onerror = function() { reject(new Error('Could not load image at ' + url)); }; image.src = url; }); }
上面代码中,使用
Promise
包装了一个图片加载的异步操作。如果加载成功,就调用resolve
方法,否则就调用reject
方法。
-
- 下面是一个用
Promise
对象实现的 Ajax 操作的例子。-
const getJson = (url) => { return new Promise(function(resolve, reject) { const handler = function() { if (this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; const client = new XMLHttpRequest(); client.open('get', url); client.onreadystatechange = handler; client.responseType = 'json'; client.setRequestHeader("Accept", "application/json"); client.send(); }) } getJson("./posts.json").then((json) => { console.log('json :', json) }, err => { console.log('err :', err); })
-
-
上面的代码中,getJson 是对XMLHttpRequest做了一个封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回了一个promise对象.需要注意的是在resolve 函数 和 reject 函数调用时都带有参.
- 如果调用 resolve 和 reject 函数时带有参数,那么他们的参数会被传递到回调函数.reject 函数的参数一般是Error对象的实例,表示抛出的错误.resolve 函数的参数除了传递正常的值之外,还可能是一个Promise对象,像下面的例子:
-
const p1 = new Promise((resolve, reject) => { // todo }); const p2 = new Promise((resolve, reject) => { resolve(p1); });
上面代码中,
p1
和p2
都是 Promise 的实例,但是p2
的resolve
方法将p1
作为参数,即一个异步操作的结果是返回另一个异步操作。
-
- 这里需要注意的是 p1 和p2 都是Promise 的实例, 这时候 p1作为p2 resolve 的参数,就是说 p1 的状态决定 p2 的状态.如果p1 的结果是 pending , 那么p2 就会等待p1 的状态改变而改变,若p1 是 resolved 或者 rejected 中任意一种,那么p2 的回调函数就会立即执行.
-
const p1 = new Promise((resolve, reject) => { // todo setTimeout(() => reject(new Error('p1抛出的错误信息')), 3000); }); const p2 = new Promise((resolve, reject) => { setTimeout(() => resolve(p1), 1000); }); p2.then((result) => { console.log('result :', result); }).catch((err) => { console.log('err :', err); });
上述代码中,在p1中写了个定时器 3s后执行抛出异常.p2是在1s 后执行resolve 返回的是p1.由于p2返回的是一个Promise,导致自己的状态无效了 ,等待p1的状态而确定p2的状态,所以后面的then() 语句是针对p1 的.又过了两秒p1执行reject() 抛出一个错误,状态变成rejected,导致出发catch方法指定的回调函数.
- 注意 调用resolve 和 reject 并不会终结Promise的参数函数的执行.
-
new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { console.log(r); }); // 2 // 1
-
- 上述代码中,执行resolve(1) 后 console.log(2) 还是回执行,并且先打印出来.因为promise 里面的resolve 是在本轮事件循环尾执行,总是晚于本轮循环的同步任务.
- 一般来说调用 resolve 或者 reject 后 Promise 的使命就完成了,后续的操作我们应该放在then()方法里面,而不应该写在resolve / reject 后面.所以最好在他们前面添加 return 语句,这样既不会有意外了
-
new Promise((resolve, reject) => { return resolve(1); // 后面的语句不会执行 console.log(2); })
-
- ES6 规定,
-
Promise.prototype.then()
- Promise 实例具有
then
方法,也就是说,then
方法是定义在原型对象Promise.prototype上的.他的作用是为了Promise实例添加状态改变时的回调函数. - then 方法返回的是一个新的Promise 实例(注意: 不是原来的Promise). 因为可以采取链式写法,即then方法后面在调用另一个then 方法.
- eg:
getJSON("/posts.json")
.then(function(json) { return '123'; })
.then(function(post) { console.log(post); }); // 123
- eg:
- 上述代码中 若是 return 的是一个异步操作,则第二个then 会等待return 的异步的返回的结果.给个栗子:
-
getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function funcA(comments) { console.log("resolved: ", comments); }, function funcB(err){ console.log("rejected: ", err); });
-
- Promise 实例具有
-
Promise.prototype.catch()
-
Promise.prototype.catch() 是 .then(null,rejection)的别名,用于指定发生错误时候的回调函数.
-
getJSON('/posts.json').then(function(posts) { // ... }).catch(function(error) { // 处理 getJSON 和 前一个回调函数运行时发生的错误 console.log('发生错误!', error); });
-
上述代码中,getJSON方法返回一个 Promise 对象,如果该对象状态为resolved,则调用then方法指定的函数,如果异步操作操作抛出错误,状态为sejected,则会调用catch方法指定的回调函数,处理这个错误。另外,then 方法指定的函数中假若抛出错误也会被catch函数扑捉。
- eg:
const promise = new Promise(function(resolve, reject) { throw new Error('test'); }); promise.catch(function(error) { console.log(error); }); // Error: test
等价于另外两种写法
// 写法一 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
,再抛出错误是无效的。因为Promise 的状态一旦确定了是无法改变的。 - Promise 对象的错误具有"冒泡"性质,也就是说会一层一层往外传,错误总会被下一个catch所扑捉。eg:
getJson('./file1.json') .then((data) => { getJson(data.fileUrl); }) .then((data) => { // todu }) .catch((err) => { console.log('err :', err); });
上述代码中 一共有三个 Promise , 一个是getJson 产生的 两个是 then 产生的 它们三个中任意一个抛出的错误异常都会被catch扑捉。
- 之前在then方法里写 两个函数,一个是resolve的回调函数,另一个是 reject 的回调函数。但是通常我们不建议这样写,不在then方法里面写 reject 的回调,我们一般是用catch 方法来捕获错误信息。这样写可以then 方法里面执行的报错。这样写也更接近同步的写法(try/catch)。跟传统的 try/catch 不同的是,Promise 如果没有catch指定处理错误信息的回调函数,promise 抛出的错误是不会传递到外层代码,即不会有任何反应。
-
-
应用
冒泡