1.promise的含义
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。
所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise
对象有以下两个特点。
(1)对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了Promise
对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise
对象提供统一的接口,使得控制异步操作更加容易。
Promise
也有一些缺点。首先,无法取消Promise
,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。第三,当处于pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
2.小试promise用法:
Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
resolve
函数的作用是,将Promise
对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject
函数的作用是,将Promise
对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
var p=new Promise(function(resolve,rejcet){setTimeout(function(){if(true){//异步操作成功resolve('success');}else{//异步操作失败rejcet('failure');}},1000);});p.then(function(value){//成功的回调console.log(value);},function(error){//失败的回调console.log(error);});复制代码
从demo中可以看到,在ES6中Promise
对象是一个构造函数,用来生成Promise
实例。并且Promise
实例生成以后,可以用then
方法分别指定resolved
状态和rejected
状态的回调函数。
3.手写一个promise,彻底掌握它的原理
首先,我们先搭建好代码的骨架:
function Promise(callback) {var self = this //promise实例self.status = 'PENDING' // Promise当前的状态self.data = undefined // Promise的值self.onResolvedCallback = [] // Promise resolve时的回调函数集self.onRejectedCallback = [] // Promise reject时的回调函数集callback(resolve, reject) // 执行executor并传入相应的参数function resolve(value){}function reject(error){}}// 添加我们的then方法Promise.prototype.then=function(){}复制代码
我们先创建一个Promise构造函数,并传入一个回调函数callback,callback里面传入两个函数作为参数,一个是resove,一个是reject。并在Promise的原型上加入我们的then方法。框架搭好了,接下来我们来一点点的完善框架里面的内容,可以这么说,把resolve,reject和then补充完,基本可以说就是把Promise完成了。
然后紧接着,我们完善一下resolve和reject:
function Promise(callback) {var self = thisself.status = 'PENDING' // Promise当前的状态self.data = undefined // Promise的值self.onResolvedCallback = [] // Promise resolve时的回调函数集self.onRejectedCallback = [] // Promise reject时的回调函数集callback(resolve, reject) // 执行executor并传入相应的参数function resolve(value){if(self.status=='PENDING'){self.status=='FULFILLED';self.data=value;// 依次执行成功之后的函数栈for(var i = 0; i < self.onResolvedCallback.length; i++) {self.onResolvedCallback[i](value)}}}function rejecte(error){if (self.status === 'PENDING') {self.status = 'REJECTED'self.data = error;// 依次执行失败之后的函数栈for(var i = 0; i < self.onRejectedCallback.length; i++) {self.onRejectedCallback[i](error)}}}}复制代码
接下来我们实现我们的then方法:
then方法是Promise的核心,因此这里会花比较大的篇幅去介绍then:
一个promise的then接受两个参数:
promise.then(onFulfilled, onRejected)复制代码
onFulfilled
和onRejected
都是可选参数。
如果onFulfilled
不是函数,其必须被忽略如果onRejected
不是函数,其必须被忽略
onFulfilled 特性
如果onFulfilled
是函数:
当promise
执行结束后其必须被调用,其第一个参数为promise
的终值,也就是resolve传过来的值在promise
执行结束前其不可被调用其调用次数不可超过一次
onRejected 特性
如果onRejected
是函数:
当promise
被拒绝执行后其必须被调用,其第一个参数为promise
的据因,也就是reject传过来的值在promise
被拒绝执行前其不可被调用其调用次数不可超过一次
调用时机
onFulfilled
和onRejected
只有在执行环境堆栈仅包含平台代码时才可被调用(平台代码指引擎、环境以及 promise 的实施代码)
调用要求
onFulfilled
和onRejected
必须被作为函数调用(即没有this
值,在严格模式(strict)中,函数this
的值为undefined
;在非严格模式中其为全局对象。)
多次调用
then
方法可以被同一个promise
调用多次
当promise
成功执行时,所有onFulfilled
需按照其注册顺序依次回调当promise
被拒绝执行时,所有的onRejected
需按照其注册顺序依次回调
返回
then
方法必须返回一个promise
对象
promise2 = promise1.then(onFulfilled, onRejected);复制代码
如果onFulfilled
或者onRejected
返回一个值x
,则运行下面的Promise 解决过程:[[Resolve]](promise2, x)
如果onFulfilled
或者onRejected
抛出一个异常e
,则promise2
必须拒绝执行,并返回拒因e
如果onFulfilled
不是函数且promise1
成功执行,promise2
必须成功执行并返回相同的值如果onRejected
不是函数且promise1
拒绝执行,promise2
必须拒绝执行并返回相同的据因
不论promise1
被 reject 还是被 resolve 时promise2
都会被 resolve,只有出现异常时才会被 rejected。
每个Promise对象都可以在其上多次调用then方法,而每次调用then返回的Promise的状态取决于那一次调用then时传入参数的返回值,所以then不能返回this,因为then每次返回的Promise的结果都有可能不同。
接下来我们来写我们的then方法:
Promise.prototype.then = function(onResolved, onRejected) {var self = thisvar promise2// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}if (self.status === 'resolved') {// 如果promise1(此处即为this/self)的状态已经确定并且是resolved,我们调用onResolved// 因为考虑到有可能throw,所以我们将其包在try/catch块里return promise2 = new Promise(function(resolve, reject) {try {var x = onResolved(self.data)if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为promise2的结果x.then(resolve, reject)}resolve(x) // 否则,以它的返回值做为promise2的结果} catch (e) {reject(e) // 如果出错,以捕获到的错误做为promise2的结果}})}// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数,就不再做过多解释if (self.status === 'rejected') {return promise2 = new Promise(function(resolve, reject) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})}if (self.status === 'pending') {// 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected,// 只能等到Promise的状态确定后,才能确实如何处理。// 所以我们需要把我们的**两种情况**的处理逻辑做为callback放入promise1(此处即this/self)的回调数组里// 逻辑本身跟第一个if块内的几乎一致,此处不做过多解释return promise2 = new Promise(function(resolve, reject) {self.onResolvedCallback.push(function(value) {try {var x = onResolved(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})self.onRejectedCallback.push(function(reason) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})})}}// 为了下文方便,我们顺便实现一个catch方法Promise.prototype.catch = function(onRejected) {return this.then(null, onRejected)}复制代码
最后附上完整的代码:
Promise.prototype.then = function(onResolved, onRejected) {var self = thisvar promise2// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}if (self.status === 'resolved') {// 如果promise1(此处即为this/self)的状态已经确定并且是resolved,我们调用onResolved// 因为考虑到有可能throw,所以我们将其包在try/catch块里return promise2 = new Promise(function(resolve, reject) {try {var x = onResolved(self.data)if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为promise2的结果x.then(resolve, reject)}resolve(x) // 否则,以它的返回值做为promise2的结果} catch (e) {reject(e) // 如果出错,以捕获到的错误做为promise2的结果}})}// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数,就不再做过多解释if (self.status === 'rejected') {return promise2 = new Promise(function(resolve, reject) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})}if (self.status === 'pending') {// 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected,// 只能等到Promise的状态确定后,才能确实如何处理。// 所以我们需要把我们的**两种情况**的处理逻辑做为callback放入promise1(此处即this/self)的回调数组里// 逻辑本身跟第一个if块内的几乎一致,此处不做过多解释return promise2 = new Promise(function(resolve, reject) {self.onResolvedCallback.push(function(value) {try {var x = onResolved(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})self.onRejectedCallback.push(function(reason) {try {var x = onRejected(self.data)if (x instanceof Promise) {x.then(resolve, reject)}} catch (e) {reject(e)}})})}}// 为了下文方便,我们顺便实现一个catch方法Promise.prototype.catch = function(onRejected) {return this.then(null, onRejected)}复制代码