全面理解Javascript中Promise
最近在学习Promise的时候,在网上收集了一些资料,发现很多的知识点不够系统,所以小编特意为大家整理了一些自认为
比较好的文章,供大家更好地学习js中非常有趣的Promise
Promise概念
2015 年 6 月,ECMAScript 6 的正式版 终于发布了。
ECMAScript 是 JavaScript 语言的国际标准,javascript 是 ECMAScript 的实现。ES6 的目标,是使得 JavaScript 语言可以用来编写大型的复杂的应用程序,成为企业级开发语言。
–Promise知识JS的一个新对象,为的就是解决js在处理异步的时候产生的多成回调的问题,那么在Promise纳入ES6的规范之前,一些优秀
的框架是如何解决这种异步回调的问题的呢?
在jQuery这个传奇的框架中,已经有方法解决上述问题的方案了,那就是jquery的deferred对象,还没有接触到这个对象的小同学可以想去
了解一下,推荐一篇博文jQuery的deferred对象详解。当我了解到JQ中
deferred对象的时候,就发现Promise的奥秘其实很简单,跟deferred对象的实现方法有着异曲同工之妙。下面开始从Promise的基础,Promise应用出发带大家了解Promise的美妙
Promise基础
–ES6 原生提供了 Promise 对象。
所谓 Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。
Promise 对象有以下两个特点。
(1)对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
var promise = new Promise(function(resolve, reject) { if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } }); promise.then(function(value) { // success }, function(value) { // failure });
Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。
如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved);
如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。
使用Promise组织你的AJAX
//创建一个Promise实例,获取数据。并把数据传递给处理函数resolve和reject。需要注意的是Promise在声明的时候就执行了。 var getUserInfo=new Promise(function(resolve,reject){ $.ajax({ type:"get", url:"index.aspx", success:function(data){ if(data.Status=="1"){ resolve(data.ResultJson)//在异步操作成功时调用 }else{ reject(data.ErrMsg);//在异步操作失败时调用 } } }); }) //另一个ajax Promise对象, var getDataList=new Promise(function(resolve,reject){ $.ajax({ type:"get", url:"index.aspx", success:function(data){ if(data.Status=="1"){ resolve(data.ResultJson)//在异步操作成功时调用 }else{ reject(data.ErrMsg);//在异步操作失败时调用 } } }); }) //Promise的方法then,catch方法 getUserInfo.then(function(ResultJson){ //通过拿到的数据渲染页面 }).catch(function(ErrMsg){ //获取数据失败时的处理逻辑 }) //Promise的all方法,等数组中的所有promise对象都完成执行 Promise.all([getUserInfo,getDataList]).then(function([ResultJson1,ResultJson2]){ //这里写等这两个ajax都成功返回数据才执行的业务逻辑 })
写一个自己的Promise,揭开Promise的神秘面纱
–Promise的规范
1.一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)
2.一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换
3.promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致
4.then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。
–promise原理分析
可以看到promise的规范并不是很多,下面我们一边分析promise一边自己写一个promise的实现。Promise实现的大致思路如下:
构造函数Promise接受一个函数resolver,可以理解为传入一个异步任务,resolver接受两个参数,一个是成功时的回调,一个是失败时的回调,这两参数和通过then传入的参数是对等的。
其次是then的实现,由于Promise要求then必须返回一个promise,所以在then调用的时候会新生成一个promise,挂在当前promise的_next上,同一个promise多次调用都只会返回之前生成的_next。
由于then方法接受的两个参数都是可选的,而且类型也没限制,可以是函数,也可以是一个具体的值,还可以是另一个promise。下面是then的具体实现:
Promise.prototype.then = function(resolve, reject) { var next = this._next || (this._next = Promise()); var status = this.status; var x; if('pending' === status) { isFn(resolve) && this._resolves.push(resolve); isFn(reject) && this._rejects.push(reject); return next; } if('resolved' === status) { if(!isFn(resolve)) { next.resolve(resolve); } else { try { x = resolve(this.value); resolveX(next, x); } catch(e) { this.reject(e); } } return next; } if('rejected' === status) { if(!isFn(reject)) { next.reject(reject); } else { try { x = reject(this.reason); resolveX(next, x); } catch(e) { this.reject(e); } } return next; } };