总想着王者荣耀排位赛再提升个等级就弃掉游戏好好学习,然而打了两个周也没升上去,看来是应该换个方向发挥了。
最近看了《javascript Promise迷离书》,对Promise的理解颇有加深。那么就从总结Promise开始吧。
1 什么是Promise?
抽象描述: promise是一个规范,提供了一套精心定义的、用来与代表一个可能会在任意时刻完成或失败的异步过程的结果的对象交互的接口。“Promise把异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写,而采取规定方法之外的写法都会出错。”--《javascript Promise迷离书》第一章。(大概就是知道用promise来干什么的时候才能清楚这段话的意思吧)
简单描述:promise是一个异步对象,在Promise对象创建时可能是未知的,状态变换后Promise表示一个异步操作的最终结果,与之进行交互的方式主要是 then
方法,该方法注册了两个回调函数,它允许你为异步代码执行结果的成功和失败分别绑定相应的处理方法(handlers ),用于处理resolve的value(常用变量名)或reject 的reason(常用变量名)。
1.1 Promise的构造函数(Constructor)
new Promise构造器之后,会返回一个promise对象,创建Promise对象的基本语法:
new Promise(function(resolve, reject) { // 异步处理 // 处理结束后、判定什么情况下resolve这个Promise,什么情况下reject这个Promise. // 通常resolve(value),将promise的状态转变成成功,value为之后要用的值 // 通常reject(reason),将promise的状态转变为失败,reason为之后错误处理需要用的值 if ( /* 异步操作成功的判定条件 */ ) { resolve(value); } else { reject(reason); } });
new一个Promise对象,给Promise构造函数传递的参数是一个函数(Promise构造函数只接受函数作为参数,不然是真的会报错给你看的)。该函数带有两个参数resolve和 reject。在Promise内部实现中会给这个函数参数resolve和reject传递两个回调函数。代码参考es6-promise,修改理解大概如下图中的Promise的构造函数内的resolvePromise和rejectPromise函数。resolvePromise它会对传递进来的value值进行处理,rejectPromise它会对传递的reason值进行处理。这段代码做的事情就是,new一个Promise对象,传递给Promise对象的函数内部执行了resolve(value),就相当于执行了resolvePromise(value),同理reject。
function Promise(resolver) { resolver(function resolvePromise(value) { _resolve( /*......*/ , value); }, function rejectPromise(reason) { _reject( /*......*/ , reason); }); }
那么resolve和reject主要做了什么事情了?如下图创建的testResolvePromise,在Promise构造函数接受的函数内部执行了 resolve。testResolvePromise对象的[[PromiseStatus]]值是"resolved",[[PromiseValue]]值"resolve: value"(这个字符串就是我们传入resolve函数的值)。对比testRrejectPromise,testRrejectPromise对象的[[PromiseStatus]]值是"rejected",[[PromiseValue]]值"reject:reason"(这个字符串就是我们传入reject函数的值)。resolve和reject改变了promise的状态。同时将处理后的值赋给了Promise对象的某个属性(为什么是处理后,见后文。如果你resolve的value值是一个Promise对象,那么就不是简单的赋值了。)。
总结:给Promise构造函数传递的参数是一个函数(是一个函数)。
1.2 Promise的状态
用new Promise 实例化的promise对象有三个状态: pending,fulfilled,rejected。
pending: promise对象刚被创建的初始状态,既未完成也没有失败的状态,此状态可以迁移至fulfilled和rejected状态。
fulfilled:意味着操作成功完成,resolve(成功)时,此时的状态不能迁移(不能改变的)。
rejected:意味着操作失败reject(失败)时,此时的状态不能迁移(不能改变的)。
eg: 如图创建了一个testPromisePending对象,用setTimeout设置一个时间10秒后再执行resolve。在这时间段前还没有执行resolve,此时的testPromisePending的[[PromiseStatus]]值是"pending"。10秒过后再在控制太输出一次testPromisePending,此时它的状态就迁移了[[PromiseStatus]]值是"resolved",即是fulfilled状态。
“promise对象的状态,从Pending转换为Fulfilled或Rejected之后, 这个promise对象的状态就不会再发生任何变化”,--《javascript Promise迷离书》(书中的流程图十分的清晰,有利于promise的工作流程理解)
pending状态---->resolve(value)----->fulfilled状态.
pending状态---->reject(reason)----->rejected状态.
总结:如果一个promise不是pending状态,就说明这个promise是settled(不变的),它要么fulfilled状态要么是rejected状态。
1.3 Promise的then方法
把这个方法单独拿出来说,只是为了承接上文,Promise构造函数接受一个函数作为参数,函数里面内部代码执行了resolve或reject后续可以干什么,有resolve(value)或 reject(reason)传递了值后续怎么处理?这时候then方法闪亮登场了,promise的then方法里面可以设置resolve(value) 或reject(reason)时调用的回调函数。then方法接受两个可选参数,当两个参数不是函数就会被忽略掉。
var testPromise = new Promise(function(resolve, reject) { if ( /* 异步操作成功的判定条件 */ ) { resolve(value); //函数内部resolve了value值,那么我们怎么处理value值了; } else { reject(error); //函数内部reject了reason值,那么我们怎么处理reason值了; } }); testPromise.then(function onFulfilled(value) { //当testPromise的状态是fulfilled的时候执行 }, function onRejected(reson) { //当testPromise的状态是rejected的时候执行 })
pending状态---->resolve(value)----->fulfilled状态---->执行then方法里面onFulfilled(value)方法
pending状态---->reject(reason)----->rejected状态---->执行then方法里面onRejected(reason)方法
当testPromise的状态迁移成 fulfilled或rejected的时候时才执行后续的then方法。testPromise的状态是fulfilled就执行onFulfilled方法,此时的value参数的值就是之前resolve的值,onFulfilled函数内部就可以对fulfilled的promise和传递进来value值进行后续的处理了。testPromise的状态是rejected就执行onRejected方法,此时的reason参数的值就是之前reason的值,onRejected函数内部就可以对rejected的promise和传递进来reason值进行后续的处理了。 (上面的路线只会执行一条,)
对了then执行完后返回的结果还是一个promise对象,妈呀这么执行下去promise是不是没完没了呀?对呀对呀这就是后续为什么promise适合处理某些场景的原因之一(加粗,加粗,后面需要解释)。每次调用then都会返回一个新创建的promise,神奇了!!还有更更神奇的事儿,这个新创建的promise跟then方法执行的回调onFulfilled和onRejected有关系(废话)。
⑴.调用then方法会返回的promise是新创建的
⑵.这个新创建promise的值跟onFulfilled和onRejected的函数内部有无return 以及return的值有关系。如果没有return则返回一个状态为fulfilled的[[PromiseValue]](不同实现内部属性不一定叫PromiseValue)为undefined的promise对象。
⑶.承接⑵如果有return,retuen 一个普通的object对象那么新创建promise对象的 [[PromiseValue]] 的值就等于 object。 如果 return 的是一个Promise对象,还是会返回一个新的promise对象,且属性值和return 的Promise对象值一样
返回了一个普通的promise,imANewPromiseB长什么样子
var testPromiseA = new Promise(function(resolve, reject) { resolve("testPromiseA") }); var testForreturnPromise = new Promise(function(resolve, reject) { resolve("just test for testPromiseB") }); //返回了一个普通的promise,imANewPromiseB长什么样子 var imANewPromiseB = testPromiseA.then(function onFulfilled(value) { //返回了一个promise return testForreturnPromise }) // 在控制台输出,imANewPromiseB和 testForreturnPromise 的属性值一样,但他们不是同一个promise。 //imANewPromiseB 是一个新的promise 对象
在控制台输出,imANewPromiseB和 testForreturnPromise 的属性值一样,但他们不是同一个promise,且imANewPromiseB和testPromiseA也不是同一个promise 对象。imANewPromiseB是一个新的promise。
总结:每次调用then都会返回一个新创建的promise对象。
1.4 为啥要用promise?
有一个作用就是把代码从异步回调函数拯救出来,让代码看起来逻辑清晰可爱。因为Promise把异步处理对象和处理规则进行规范化。
function getJSON(url) { return new Promise(function(resolve, reject) { let xhr = new XMLHttpRequest(); //神奇的对象 xhr.open(‘GET‘, url); xhr.onreadystatechange = handler; // 无论readyState值何时发生改变,XMLHttpRequest对象都会激发一个readystatechange事件,handler被调用,然后根据结果resolve,或者reject xhr.responseType = ‘json‘; xhr.setRequestHeader(‘Accept‘, ‘application/json‘); xhr.send(); function handler() { if (this.readyState === this.DONE) { if (this.status === 200) { resolve(this.response); //successDo(this.response) } else { reject(new Error(‘getJSON: `‘ + url + ‘` failed with status: [‘ + this.status + ‘]‘)); //faileDo(this.response) } } }; }); } // 使用promise getJSON(url).then(function onFulfilled(value) { //successDo }, function onRejected(reson) { //faileDo }) // 使用回调 getJSON(url, successDo, faileDo)
使用回调的方式来做getJSON,拿数据和对拿到数据成功和失败都在一个函数里面操作处理。
使用promise来做getJSON,getJSON只需要做好自己拿数据,且通过resolve和reject返回拿数据成功还是失败的逻辑,并不关心成功或失败的处理。后续的then方法会根据getJSON返回的promise的状态执行onFulfilled方法或者onRejected方法来处理,数据拿成功或者数据拿失败的逻辑。这个流程比较符合人类(我这种人类)的习惯,一步一步执行操作,拿数据返回成功或失败----->处理拿数据成功或失败。
总结:Promise 可以让异步处理对象更像一个流程操作。使用Promise 的时候要思考Promise 的适用场景。并不是说在异步处理的时候Promise永远都是最好的选择。