引言 Promise 是异步编程的一种解决方案,比传统的解决方案——回调和事件——更合理且强大。最近的项目要用到这个,就参照阮一峰老师的《ES6标准入门》这本书简单学一下了。javascript
所谓 Promise ,简单来讲就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上来看,Promise 是一个对象,从它能够获取异步操做的消息。Promise 提供统一的 API,各类异步操做均可以用一样的方法进行处理。java
Promise 对象有如下两个特色。es6
ES6 规定,Promise 对象是一个构造函数,用来生成 Promise 实例。
举个例子。ajax
var promise = new Promise(function (resolve, reject) { // some code if (/* 异步操做成功*/) { resolve(value); } else { // 异步操做失败 reject(error); } });
Promise 构造函数接收一个函数
做为参数,该函数
的两个参数分别是 resolve
和 reject
。他们是两个函数,由 Javascript 引擎提供,不用本身部署。编程
resolve
函数的做用是将 Promise
对象的状态从『未完成』(Pending)变为『成功』(Resolved),在异步操做成功的时候调用,并将异步操做的结果做为参数传递过去。 reject
函数的做用是,将 Promise
对象的状态从『未完成』(Pending)变为『失败』(Rejected)json
当咱们生成了一个 Promise 实例以后。就能够用 then
方法分别指定 Resolved
状态和 Rejected
状态的回调函数。数组
promise.then(function (value) { // success console.log(value); }, function (error) { // failed console.log(error); });
then
方法能够接受两个回调函数做为参数。第一个回调函数是 Promise
对象的状态变为 Resolved
时调用,第二个回调函数是 Promise
对象的状态变为 Rejected
时调用。其中第二个参数是可选的,不必定要提供。这两个函数都接收 Promise
对象传出的值做为参数。promise
咱们来个小例子异步
let promise = new Promise(function(resolve,reject){ console.log('Promise'); let value = 'value'; resolve(value); }); promise.then(function(value){ console.log(value); }); console.log('Hi'); // Promise // Hi // value
上面的代码中,Promise 新建后会当即执行,因此首先输出的是 Promise
。而后,then
方法指定的回调函数将当前脚本全部同步任务执行完成后才会执行,因此 Resolved
最后输出。异步编程
Promise 实例具备 then
方法,即 then
方法是定义在原型对象 Promise.prototype
上的。它的做用是为 Promise 实例添加改变状态时的回调函数。前面说过,then
方法的第一个参数是 Resolved
状态的回调函数,第二个参数(可选)是 Rejected
状态的回调函数。
then
方法返回的是一个新的 Promise 实例
(注意
不是原来的那个 Promise 实例)。所以能够采用链式写法,即 then
方法后面再调用另外一个 then
方法。
getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { // ... });
上面的代码使用 then
方法依次指定了两个回调函数。第一个回调函数完成之后,将会返回结果做为参数,传入第二个回调函数。
而后采用链式的 then
能够指定一组按顺序调用的回调函数。这时,前一个回调函数有可能返回的仍是一个 Promise 对象(即有异步操做),然后一个回调函数就会等待该 Promise 对象的状态发生变化,再被调用。
Promise.prototype.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); });
由上面能够看出, reject
方法的做用等同于抛出错误。
若是 Promise
状态已经变成 Resolved,在抛出错误是无效的。
const promise = new Promise(function(resolve, reject) { resolve('ok'); // Promise 状态已变成 已完成 throw new Error('test'); }); promise .then(function(value) { console.log(value) }) .catch(function(error) { console.log(error) }); // ok
注意 通常来讲,不要在 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
方法的第二个参数。
Promise.all
方法是将多个 Promise 对象实例包装成一个新的实例。
var p = Promise.all([p1, p2, p3]);
上面的代码中,Promise.all()
方法接受一个数组做为参数,p1, p2, p3 都是 Promise 对象的实例。若是不是,就会先调用下面讲到的 Promise.resolve
方法,将参数转换为 Promise 实例,再进一步处理(Promise.all
方法的参数不必定是数组,可是必须具备 Iterator
接口,且每一个返回成员都是 Promise 实例)。
p 的状态由 p1, p2, p3 决定,分红两种状况
下面是一个具体例子。
var promises = [2,3,4,5].map(function(id){ console.log(id) }); Promise.all(promises).then(function(res){ console.log(res); resolve }).catch(function(error){ console.log(error); }); // 先执行全部 promise 实例的异步操做,而后吧操做的结果打包数组返回 // 2 3 4 5 [undefined,undefined,undefined,undefined]
上面的代码中,Promise 是包含 6 个 Promise 实例的数组,只有这 6 个实例的状态都变成 fulfilled,或者其中有 1 个变成 rejected,才会调用 Promise.all
方法后面的回调函数。
Promise.race
方法一样是将多个 Promise 实例包装成一个新的 Promise 实例。
var p = Promise.race([p1, p2, p])
上面的代码中,只要 p1, p2, p3 中 有一个实例
率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值就传递给 p 的回调函数。Promise.race
方法的参数与 Promise.all
方法同样,若是不是 Promise 实例,就会先调用下面讲到的 Promise.resolve
方法,将参数转为 Promise 实例,再进一步处理。
下面是一个例子,若是指定时间内没有得到结果,就将 Promise 的状态变成 Rejected,不然就变为 Resolved。
const p = Promise.race([ fetch('/resource-that-may-take-a-while'), new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('request timeout')), 5000) }) ]); p .then(console.log) .catch(console.error);
上面代码中,若是 5 秒以内 fetch
方法没法返回结果,变量 p 的状态就会变为 rejected,从而触发 catch
方法指定的回调函数。
有时须要将现有对象转为 Promise 对象,Promise.resolve
方法就起到这个做用。
const jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将 jQuery 生成的 deferred 对象,转为一个新的 Promise 对象。
Promise.resolve
等价于下面的写法。
Promise.resolve('foo') // 等价于 new Promise(resolve => resolve('foo'))
Promise.resolve方法的参数分红四种状况。
若是参数是一个 Promise 实例,那么 Promise.resolve
将不作任何修改,原封不动的返回这个实例。
thenable 对象是指具备 then 方法的对象,例以下面这个对象
let thenable = { then: function(resolve, reject) { resolve(42); } };
Promise.resolve
方法会将这个对象转为 Promise 对象,任何执行 thenable 对象的 then 方法。
let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 });
上面的代码中, thenable 对象的 then 方法执行后,对象 p1 的状态就变为 resolved,从而当即执行最后的 then 方法指定的回调函数。输出 42。
若是参数是一个原始值,或者是一个不具备 then 方法的对象,那么 Promise.resolved
方法返回一个新的 Promise 对象,状态为 Resolved。
const p = Promise.resolve('Hello'); p.then(function (s){ console.log(s) }); // Hello
上面代码生成一个新的 Promise 对象的实例 p。因为字符串 Hello 不属于异步操做(判断方法是字符串对象不具备 then 方法),返回 Promise 实例的状态从一辈子成就是 resolved,因此回调函数会当即执行。Promise.resolve
方法的参数,会同时传给回调函数。
Promise.resolved
方法容许在调用时不带有参数,而直接返回一个 Resolved 状态的 Promise 对象。
因此,若是你但愿获得一个 Promise 对象,比较方便的方法就是直接调用 Promise.resolve
方法。
const p = Promise.resolve(); p.then(function () { // ... });
上面代码中的 p 就是一个 Promise 对象。
须要注意的是,当即 resolve 的 Promise 对象实在本轮 『事件循环』(event loop)结束时,而不是在下一轮『事件循环』开始时。
setTimeout(function () { console.log('three'); }, 0); Promise.resolve().then(function () { console.log('two'); }); console.log('one'); // one // two // three
上面代码中,setTimeout(fn, 0)
在下一轮『事件循环』开始时执行,Promise.resolve()
在本轮『事件循环』结束时执行,console.log('one')
则是当即执行,所以最早输出。
Promise.reject(resson)
方法也会返回一个新的 Promise 实例,状态为 Rejected (这个就暂时想不懂怎么应用了)。
const p = Promise.reject('出错了'); // 等同于 const p = new Promise((resolve, reject) => reject('出错了')) p.then(null, function (s) { console.log(s) }); // 出错了
上面的代码生成一个 Promise 对象的实例 p,状态为 Rejected,回调函数会当即执行。
以上就是关于 Promise 学习的内容,若有错误的地方就请在下面评论处,发表一下见解,固然也能够放一下关于进阶学习 Promise 的文章,你们一块儿学习。