在以前翻博客时,看到promise,又重读了一边,忽然发现理解很浅,记的笔记也不是很好理解,又从新学习promise,加深理解,学以至用
在promise出来以前,js经常使用解决异步方式都是采用回调函数方式,可是若是需求过多,会造成一系列的回调函数,俗称:回调地狱。致使后期阅读和维护代码特别麻烦。因此es6的Promise就是为了解决这个麻烦而出来的新对象,以前早就存在,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。javascript
Promise对象是为了简化异步编程。解决回调地狱状况。
Promise,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。重点是取决与这个事件以后的一系列动做,then()或catch()的等等。
从语法上说,Promise 是一个对象,从它能够获取异步操做的消息。Promise对象用于延迟(deferred) 计算和异步(asynchronous ) 计算。一个Promise对象表明着一个还未完成,但预期未来会完成的操做。这样表示了一旦用了promise对象,就不能退出,直到出现结果为止(resloved或rejected)
Promise是一个对象,能够用构造函数来建立一个Promise实例。java
let promise = new Promise((resolve, reject) =>{ // .... some coding if (true){ // 异步操做成功 resolve(value); } else { reject(error); } }) promise.then(value=>{ // 成功的回调函数 }, error=>{ // 失败后的回调函数 }) console.log(typeof promise) // object
params:传参是一个回调函数。这个回调函数有两个参数resolve和reject。git
成功了的执行
)失败了的执行
)// 成功时 let promise = new Promise((resolve, reject) =>{ console.log('开始') if (2 > 1){ // 异步操做成功 resolve({name:'peter',age:25}); } else { reject(error); } }) promise.then(value=>{ // 成功的回调函数 console.log(value) }, error=>{ // 失败后的回调函数 console.log(error) }) // 开始 // {name: "peter", age: 25}
// 失败时 let promise = new Promise((resolve, reject) =>{ console.log('开始') if (2 > 3){ // 异步操做成功 resolve(a); } else { reject('未知错误'); } }) promise.then(value=>{ // 成功的回调函数 console.log(value) }, error=>{ // 失败后的回调函数 console.log(error) }) // 开始 // 未知错误
ps:Promise实例化一个对象后,会当即实行。es6
new Promise((resolve, reject)=>console.log('promise')); console.log('123'); // promise // 123
这个结果发现,先执行promise后执行123。github
若是改变已经发生了,你再对Promise对象添加回调函数,也会当即获得这个结果。这与事件(Event)彻底不一样,事件的特色是,若是你错过了它,再去监听,是得不到结果的
ajax
promise先按顺序实行完promise实例中方法再实行then中的resolve或者reject。编程
let promise = new Promise((resolve, reject)=>{ console.log('promise') if (2 > 1){ // 异步操做成功 resolve({name:'peter',age:25}); } else { reject(error); } console.log('end') }) promise.then( value=>{ console.log(value) }, error=>{ console.log(error) } ) // promise // end // {name: "peter", age: 25}
ajax是最多见的异步操做方式,那么用promise封装Ajax的例子json
const getJSON = function (url) { const promise = 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(); }); return promise; }; getJSON("xxxxx").then(function (value) { console.log('Contents: ' + value); }, function (error) { console.error('出错了', error); });
then() 为 Promise 实例添加状态改变时的回调函数,上面已经提起过。
params解释:segmentfault
一般状况下,then方法做为成功时的回调方法,catch方法做为失败时回调方法。catch()在后面,能够理解为then方法中的reject参数数组
let promise = new Promise((resolve, rejected)=>{ if(2<3){ resolve() }else{ rejected() } }) promise.then(resolve=>{ console.log('right') }).catch(reject=>{ console.log('error') })
ps: then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。
var aPromise = new Promise(function (resolve) { resolve(100); }); var thenPromise = aPromise.then(function (value) { console.log(value); }); var catchPromise = thenPromise.catch(function (error) { console.error(error); }); console.log(aPromise !== thenPromise); // => true console.log(thenPromise !== catchPromise);// => true
因此每个then()方法就是一个新promise对象。所以能够采用链式写法,即then方法后面再调用另外一个then方法。这样必需要传一个参数过去。
promise的链式编程,就是第一个的Promise实例的返回的值做为下一个Promise实例的参数。
function start() { return new Promise((resolve, reject) => { resolve('start'); }); } start() .then(data => { // promise start console.log(data); return Promise.resolve(1); // 1 }) .then(data => { // promise 1 console.log(data); }) // start // 1
从上面例子可知:
catch()和then()都是挂载在promise对象的原型上的。
Promise.prototype.catch
方法是promise.then(null, rejection
)或promise.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
通常是等价于:(在遇到失败的状况下)
Promise.catch()
<=> promise.then(null,e=>reject())
若是异步操做抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。另外,then方法指定的回调函数,若是运行中抛出错误,也会被catch方法捕获。
const promise = new Promise(function(resolve, reject) { throw new Error('test'); }); promise.catch(function(error) { console.log(error); }); // Error: test
Promise 对象的错误具备“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误老是会被下一个catch语句捕获。
function throwError(value) { // 抛出异常 throw new Error(value); } // <1> onRejected不会被调用 function badMain(onRejected) { return Promise.resolve(42).then(throwError, onRejected); } // <2> 有异常发生时onRejected会被调用 function goodMain(onRejected) { return Promise.resolve(42).then(throwError).catch(onRejected); } // 运行示例 badMain(function(){ console.log("BAD"); }); goodMain(function(){ console.log("GOOD"); }); // GOOD
在上面的代码中, badMain 是一个不太好的实现方式(但也不是说它有多坏), goodMain 则是一个能很是好的进行错误处理的版本。
为何说 badMain 很差呢?,由于虽然咱们在 .then 的第二个参数中指定了用来错误处理的函数,但实际上它却不能捕获第一个参数 onFulfilled 指定的函数(本例为 throwError )里面出现的错误。也就是说,这时候即便 throwError 抛出了异常,onRejected 指定的函数也不会被调用(即不会输出"BAD"字样)。
与此相对的是, goodMain 的代码则遵循了 throwError→onRejected 的调用流程。 这时候 throwError 中出现异常的话,在会被方法链中的下一个方法,即 .catch 所捕获,进行相应的错误处理。
.then 方法中的onRejected参数所指定的回调函数,实际上针对的是其promise对象或者以前的promise对象,而不是针对 .then 方法里面指定的第一个参数,即onFulfilled所指向的对象,这也是 then 和 catch 表现不一样的缘由。(详见Javascript Promise 迷你版)
这个是从别人的博客拿来的代码和解释,了那么多,总结为,catch可以捕获它以前的异常,而在then()方法中第二个参数是没办法捕获到的,由于实行了resolve方法。
看字面量的意思,是返回一个成功的promise实例。
Promise.resolve()
<=> new Promise((resolve,rejected)=>resolve())
最多见的就是将不是promise对象的异步操做转化为promise对象。
该方法有四个参数:
javascript let thenable = { then: function(resolve, reject) { resolve(42); } }; var promise = Promise.resolve(thenable) .then(value=>console.log(value));// 42
javascript var str = '17号' Promise.resolve(str).then(value=>console.log(value)) // 17号
返回一个新的 Promise 实例,该实例的状态为rejected。用法和resolve同样,可是都是以失败返回结果
Promise.reject()
<=> new Promise((resolve,reject) = >reject())
ps:Promise.reject()方法的参数,会原封不动地做为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。
const thenable = { then(resolve, reject) { reject('出错了'); } }; Promise.reject(thenable) .catch( e=> { console.log(e) }) // 返回的是thenable对象
Promise.all 接收一个 promise对象的数组做为参数,当这个数组里的全部promise对象所有变为resolve或reject状态的时候,它才会去调用 .then() 方法。
// `delay`毫秒后执行resolve function timerPromisefy(delay) { return new Promise(function (resolve) { setTimeout(function () { resolve(delay); }, delay); }); } var startDate = Date.now(); // 全部promise变为resolve后程序退出 Promise.all([ timerPromisefy(1), timerPromisefy(32), timerPromisefy(64), timerPromisefy(128) ]).then(function (values) { console.log(Date.now() - startDate + 'ms'); console.log(values); }); // 129ms // 1,32,64,128
从上述结果能够看出,传递给 Promise.all 的promise并非一个个的顺序执行的,而是同时开始、并行执行的。
假设法:若是这些promise所有串行处理的话,那么须要 等待1ms → 等待32ms → 等待64ms → 等待128ms ,所有执行完毕须要225ms的时间。但实际上不是,而是129ms左右。
和Promise.all()方法同样,参数是一个数组,可是只要有一个promise对象更改状态时就实行下一步。
// `delay`毫秒后执行resolve function timerPromisefy(delay) { return new Promise(function (resolve) { setTimeout(function () { resolve(delay); }, delay); }); } // 任何一个promise变为resolve或reject 的话程序就中止运行 Promise.race([ timerPromisefy(1), timerPromisefy(32), timerPromisefy(64), timerPromisefy(128) ]).then(function (value) { console.log(value); // => 1 });
上面的例子是1秒后就resolve了,因此直接then()了。
该方法用于指定无论 Promise 对象最后状态如何,都会执行的操做。不管resolve仍是reject都会实行的操做,不依赖其余的操做。按照执行顺序。
function promise(){ return new Promise((resolve, reject) => { resolve('success'); }) }; promise().then(data => { console.log(data) return Promise.reject('fail') }).finally(() => { console.log('end') }).catch(data =>{ console.log(data) }) // success // end // fail
从上面的例子可知,是按照promise的实行顺序执行的,在then()中,要求返回一个失败的状态,可是却没先实行失败的方法,而是按照顺序实行了finally方法。
Promise 对象的回调链,无论以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能没法捕捉到(由于 Promise 内部的错误不会冒泡到全局)。所以,咱们能够提供一个done方法,老是处于回调链的尾端,保证抛出任何可能出现的错误。
Promise.prototype.done = function (resolve, reject) { this.then(resolve, reject) .catch( function (reason) { // 抛出一个全局错误 setTimeout( () => { throw reason }, 0); }); } // 使用示例 var p = new Promise( (resolve, reject) => { resolve('p'); }); p.then(ret => { console.log(ret); return 'then1'; }).catch( err => { console.log(err.toString()); }).then( ret => { console.log(ret); return 'then2'; }).then( ret => { console.log(ret); x + 2; }).done();
该例子参考别人的例子。发现到最后直接抛出了 'Uncaught ReferenceError: x is not defined'。说明最后一个then实行时会抛出异常,也能够相似于catch方法吧。
总结来讲Promise其实就是作了一件事情,那就是对异步操做进行了封装,而后能够将异步操做以同步的流程表达出来,避免了层层嵌套的回调地狱,提供统一的接口方法,使得控制异步操做更加容易,可是也有必定的缺点,promise一旦没肯定状态,是无法终止的,一样的,也没法取消promise。
若是本文有什么不对的地方,欢迎指出,谢谢,你们一块儿进步加油。我把笔记放到github了,若是满意的话给个star。