ECMAScript6 入门 Promise
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
定义 Promise:简单来说就是一个容器,里面保存着一个未来才会结束的事情(一个异步操作)的结果 (首先他是一个对象,保存一个异步操作的消息) 特点: 1:对象的状态不受外界影响 Promise对象代表着一个异步操作 1:pending进行中 2:fulfilled已成功 3:rejected已失败 只有异步操作的结果可以决定当前是哪一种状态 (promise对象的状态由异步操作的结果决定) 2:一旦状态改变,就不会再变(从pending变为fulfilled和从pending变为rejected) (如果结果已经改变了,再去给promise对象添加回调函数,还是会得到这个结果 而事件的特点是错过了,再去监听,是得不到结果的) 缺点: 1:一旦新建它就会立即执行 2:其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部 3:当处于pending状态时,无法得知目前进展到哪一个阶段
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
基本用法 Promise对象是一个构造函数,用来生成promise实例, 构造函数接受一个函数作为参数, 参数函数又接受resolve和reject作为参数,这两货也是函数 const promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); //从pending变为fulfilled } else { reject(error); //从pending变为rejected } }); resolve函数的作用,将promise对象的状态从“未完成”变成“成功”,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去 reject函数的作用,将promise对象的状态从“未完成”变成“失败”,在异步操作成功时调用,并将异步操作报出的错误,作为参数传递出去 ** resolve和reject只是修改promise对象的状态,至于状态改变以后的回调函数,是定义在then当中
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数 then方法可以接受两个回调函数作为参数,两个函数都接受promise对象传出的值作为参数 第一个回调函数是Promise对象的状态变为resolved时调用, 第二个回调函数是Promise对象的状态变为rejected时调用,第二个函数是可选的 promise.then(function(value) { // success }, function(error) { // failure }); 1:调用resolve和reject并不会终结promise的参数函数执行,在他们两之后的代码还是会正常执行
(但是我们建议在其之后不要执行相关操作,而放在回调函数当中执行,可以再调用前面加上return) 2:当p1和p2都是promise的实例时,但是p2的resolve方法将p1作为参数的情况下,p1的状态将会决定p2的状态执行 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)) // Error: fail 最后会执行p2的catch方法
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Promise.prototype.then() 1:Promise 实例具有then方法,也就是说,then方法是定义在原型对象 2:then方法返回的是一个新的Promise实例,所以可以采用链式调用的形式,(会将前一个then方法的返回值作为下一个promise对象的传入值)
3:then的第二个参数是可选的,
可以看下then方法的源码是如何定义 this.then = function (onFulfilled, onRejected) { var self = this; return new Promise(function (resolve, reject) { return self.done(function (result) { if (typeof onFulfilled === 'function') { try { return resolve(onFulfilled(result)); } catch (ex) { return reject(ex); } } else { return resolve(result); } }, function (error) { if (typeof onRejected === 'function') { try { return resolve(onRejected(error)); } catch (ex) { return reject(ex); } } else { return reject(error); } }); }); }
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Promise.prototype.catch() Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名, 用于指定发生错误时的回调函数 另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获 promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获
所以推荐尽量then方法的第二个参数省略,将所有的错误统一使用catch进行抛出
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Promise.prototype.finally() finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作 finally方法的回调函数,不接受任何参数,所以里面的操作都是与状态没有关系 promise .then(result => {···}) .catch(error => {···}) .finally(() => {···}); 不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数 他的本质就是then方法的特例,成功和失败两种情况执行的代码一样就是finally
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Promise.all() Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例 const p = Promise.all([p1, p2, p3]); 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
Promise.race()
Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
Promise.resolve()
将一个现有的对象转换成promise对象
参数的四种情况
1:参数是promise实例,那么不做任何改变
2:thenable对象(指携带了then方法的对象),会将其转换成promise对象,并且立刻执行then方法
3:不具有then方法的对象,甚至都不是对象,返回一个新的promise对象,并且状态为resolved
4:不带有参数的情况,和3同样
Promise.reject()
将会返回一个新的promise实例,该实例的状态为rejected
注意:该方法的参数,会原封不动的作为reject的参数,就是状态为rejected时回调函数的参数
Promise.try()
我们不想区分函数是否是同步还是异步,都想用then方法指定下一步流程,catch方法处理函数抛出的错误
Promise.try(() => database.users.get({id: userId}))
.then(...)
.catch(...)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
应用 1:加载图片 const preloadImage = function (path) { return new Promise(function (resolve, reject) { const image = new Image(); image.onload = resolve; image.onerror = reject; image.src = path; }); }; 2:Generator 函数与 Promise 的结合 function getFoo () { return new Promise(function (resolve, reject){ resolve('foo'); }); } const g = function* () { try { const foo = yield getFoo(); console.log(foo); } catch (e) { console.log(e); } }; function run (generator) { const it = generator(); function go(result) { if (result.done) return result.value; return result.value.then(function (value) { return go(it.next(value)); }, function (error) { return go(it.throw(error)); }); } go(it.next()); } run(g);