之前一直对于promise,就是一顿用,从来没有深究其内部原理,无意间看到一些关于promise输出顺序的题目也是一顿瞎猜。。。哈哈,在我的同事对promise进行分享之后(虽然我因为起不来没有听),但是学习promise的意识就此种下。。。啥也不说了,就是学。。。
带着疑问去学习:
- promise是干什么的?
- promise有哪些方法?
- pomise如何实现链式调用?之前jquery也是可以链式调用,他们之前的区别?
- promise的then方法是如何确定在前面resolve或者reject执行完拿到结果再执行的?
- 手写一个满足promiseA+的promise方法(写了一下,发现无法通过promiseA+的测试用例,已放弃,拿着别人写的仔细看了一下)
promise是干什么的
promise是一种异步编程的解决方案,它可以实现把异步操作按照同步操作的流程表达出来。它可以接收一个异步请求,然后在其then方法可以得到前面异步请求的结果进行后续操作,使用链式调用的方式将异步请求的结果按照同步的方式表达出来。
理解误区
1. promise只是异步编程的解决方案,自身并不是异步请求,在new Promise()内部传的方法会立即执行。
const promise = new Promise(function (resolve, reject) { console.log('12') setTimeout(function () { resolve('success') }, 1000)})promise.then(function (success) { console.log(success)})// 执行结果: 先输出12,过了一秒后再输入success复制代码
2. new Promise内的resolve是异步执行的,且所有then方法和catch方法也会将在当前脚本所有同步任务执行完才会执行
const myPromise = new Promise(function (resolve, reject) { console.log('1') resolve('2') console.log('3')})myPromise.then(function (success) { console.log(success)})setTimeout (function () { // 这里可以看到前面myPromise已经resolved了,但是他还是会在最后执行,因为then内部也是异步操作 myPromise.then(function (success) { console.log('again', success) }) console.log('4')}, 1000)console.log('5')// 执行结果是: 1 3 5 2 4 again 2复制代码
promise有哪些方法
基本用法
const promise = new Promise(function(resolve, reject) { if (/* 异步操作成功 */){ resolve(value); } else { reject(error); }});promise.then(function(value) {}, function (error) {})复制代码
基本用法简单概括一下:
- 三种状态: - pending(进行中) - fulfilled(已成功) -rejected(已失败)
- new Promise传进的方法内resolve返回的是成功的结果,reject返回的是失败的结果
- new Promise实例化后的then方法接受两个回调函数,分别是resolve和reject的回调函数
挂载在原型上的方法
Promise.prototype.then()&&Promise.prototype.catch()
- then: 该方法内的函数在Promise状态为resolve的情况下执行,且then方法内属于异步操作,如果前面的new Promise没有resolve,then内部传进来的函数不执行
- catch:该方法内的函数在Promise状态为reject的情况下执行,且catch方法内属于异步操作,catch方法和then不太一样,它在前面new Promise()出错或者reject时,catch内部的方法都会执行。
then()和catch()默认返回的都是一个Promise实例对象,即使return了别的信息,返回的仍然是Promise实例对象,还是且这个promise的状态都是resolve
Promise.prototype.finally()
不管Promise对象最后状态如何,finally方法都会执行,(promise是pedding状态不会执行finally方法,resolve和reject都会执行)
挂载在Promise对象上的方法
Promise.resolve() && Promise.reject()
将现有对象转换为Promise对象
Promise.all()
将多个Promise实例包装成一个新的Promise实例。
var p1 = new Promise((resolve) => { setTimeout(() => { resolve('p1') }, 1000)})var p2 = new Promise((resolve) => { setTimeout(() => { resolve('p2') }, 2000)})var p3 = new Promise((resolve) => { setTimeout(() => { resolve('p3') }, 3000)})var p = Promise.all([p1, p2, p3]).then((value) => { console.log(value)}).catch((err) => { console.log(err)})// 返回值:['p1', 'p2', 'p3']复制代码
这里Promise的状态是由p1,p2,p3共同决定的:
- p1,p2和p3的状态都为resolve时,p1,p2和p3的返回值都以一个数组的形式传给后续p的then方法里
- 其中有一个的状态变为reject时,第一个被reject的实例的返回值会传递给后续p的catch方法里
- 如果前面作为参数传进来的promise实例中状态为rejected了,且其自己定义了catch方法,那么后续不会触发Promise.all()的then方法
- 如果前面作为参数传进来的promise实例有状态为rejected,且其有catch方法,那么就不会出发后面的Promise.all()的catch方法,会触发then()方法。
Promise.race()
将现有对象转换为Promise对象,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
pomise如何实现链式调用?之前jquery也是可以链式调用,他们之前的区别?
promise如何实现链式调用
在每一次执行Promise的then或者catch方法后,返回的都是一个新的Promise实例化对象,使得又一次可以使用then或者catch方法,并且可以拿到上一次then方法return回来的值。
例如:
then内部的函数return了一个数值a。我们发现then方法执行完,返回的是一个新的promise实例,其promisevalue为a
根据以上铺垫,我们就知道,其链式调用就是通过then和catch方法内部return new Promise()
jquery如何实现链式调用
通过return this,在其原型方法执行结束都会teturn其本身
var Jquery = function () {}Jquery.prototype = { css: function (attr, data) { return this }, hide: function () { console.log('hide') return this }}var jq = new Jquery()jq.css().hide()复制代码
这里jq调用了css方法,首先会在jq上找css方法,没有找到,然后就去其构造函数Jquery的原型中找,找到css方法并执行,在原型css方法里return this中的this指向的是jq
两者区别
promise执行了then或者catch方法后,每一次返回的是一个新的Promise实例,jquey一直返回的都是其本身
promise的then方法是如何确定在前面resolve或者reject执行完拿到结果再执行的
这里学习了一下别人写的promise,通过这个学习到了一些,中间关于这部分的代码
const PENDING = 'pending'const RESOLVED = 'resolved'const REJECTED = 'rejected'// promise 对象上有resolve,reject,all,race等方法,原型上有then,catch,finally方法class myPromise { constructor (fn) { // 首先需要判断fn是否是一个函数 if (typeof fn != 'function') return 'fn is not function' this.PromiseStatus = PENDING this.PromiseValue = null this.PromiseReason = null this.onResolvedCallbacks = [] this.onRejectedCallbacks = [] const resolve = value => { // 代码块一 if(value instanceof Promise) { return value.then(resolve, reject); } // new Promise构造函数传进来的函数fn会立即执行,但是在new Promise内resolve和reject是异步操作(如何做到异步操作 =>使用setTimeout确保new Promise后的then和catch方法会在fn函数执行完才触发) setTimeout(() => { if (this.PromiseStatus == PENDING) { this.PromiseStatus = RESOLVED this.PromiseValue = value this.onResolvedCallbacks.map(cb => { this.PromiseValue = cb(this.PromiseValue) }) } }) } const reject = value => { setTimeout(() => { if (this.PromiseStatus == PENDING) { this.PromiseStatus = REJECTED this.PromiseReason = value this.onRejectedCallbacks.map(cb => { this.PromiseReason = cb(this.PromiseReason) }) } }) } try { // 代码块二 // 立即执行new Promise内的 fn(resolve, reject) } catch (err) { reject(err) } } then (onResolved, onRejected) { // 代码块三 onResolved = typeof onResolved === 'function' ? onResolved : function (v) { return v} onResolved = typeof onRejected === 'function' ? onRejected : function (v) { return v} console.log('this.PromiseStatus', this.PromiseStatus) if (this.PromiseStatus === RESOLVED) { // 代码块三 条件一 return new myPromise((resolve, reject) => { // 什么时候会出现resolve一进来就变成RESOLVED或者REJECTED? 同一个Promise实例被多次调用其then时,这样会走下面的逻辑,为确定then内部还是会异步执行,使用setTimeout setTimeout(() => { try { // 执行then内部的方法 let x = onResolved(this.PromiseValue) resolvePromise(newPromise, x, resolve, reject); } catch (err) { reject(err) } }) }) } if (this.PromiseStatus === REJECTED) { // 代码块三 条件二 return new myPromise((resolve, reject) => { setTimeout(() => { try { let x = onRejected(this.PromiseReason) resolvePromise(newPromise, x, resolve, reject); } catch (err) { reject(err) } }) }) } // 当没有触发resolve或者reject时,将onResolved和onRejected存放在onResolvedCallbacks/onRejectedCallbacks集合中 if (this.PromiseStatus === PENDING) { // 代码块三 条件三 return new myPromise ((resolve, reject) => { this.onResolvedCallbacks.push(value => { try { let x = onResolved(value) resolvePromise(newPromise, x, resolve, reject); } catch (err) { reject(err) } }) this.onRejectedCallbacks.push(reason => { try { let x = onRejected(reason) resolve(x) } catch (err) { reject(err) } }) }) } }}复制代码
根据以上代码,我们进行简单的分析:
new myPromise(function (resolve, reject) { console.log(1) resolve(2)}).then(function (success) { console.log(success)})复制代码
上面的代码中,首先构造函数初始化一些值
this.PromiseStatus = PENDING // 初始promise的状态this.PromiseValue = null // 初始promise resolve状态时的value值this.PromiseReason = null // 初始promise reject状态时的value值this.onResolvedCallbacks = [] // 存储resolved状态对应的onResolvedCallbacks事件数组this.onRejectedCallbacks = [] // 存储rejected状态对应的onRejectedCallbacks事件数组复制代码
例如上面简单的例子:其经历的步骤有:
-
首先进入myPromise构造函数内,进入代码块二,立即执行myPromise传进来的函数fn,这里PromiseStatus=PENDING
try { // 代码块二 // 立即执行new Promise内的 fn(resolve, reject)} catch (err) { reject(err)}复制代码
-
执行fn时,进一步执行resolve函数,进入代码块一,这里首先对resolve传进来的参数进行了类型判断,然后我们可以看到setTimeout代码,这里将setTimeout内部的事件放在eventLoop的最后。这里PromiseStatus=PENDING
setTimeout(() => { if (this.PromiseStatus == PENDING) { this.PromiseStatus = RESOLVED this.PromiseValue = value this.onResolvedCallbacks.map(cb => { this.PromiseValue = cb(this.PromiseValue) }) } })复制代码
-
触发myPromise的then方法,进入到代码块三,进行条件判断,进入到代码块三的 条件三,返回一个新的myPromise实例,这里会立即执行下面代码
this.onResolvedCallbacks.push(value => { try { let x = onResolved(value) resolvePromise(newPromise, x, resolve, reject); } catch (err) { reject(err) }})复制代码
这个操作就是向onResolvedCallbacks里push一个函数
-
上述操作结束后,再执行前面放在eventLoop最后的事件
setTimeout(() => { if (this.PromiseStatus == PENDING) { this.PromiseStatus = RESOLVED this.PromiseValue = value this.onResolvedCallbacks.map(cb => { this.PromiseValue = cb(this.PromiseValue) }) }})复制代码
这里找到更改了PromiseStatus的状态,并执行onResolvedCallbacks数组中的函数,这个函数是之前代码块三中的
value => { try { let x = onResolved(value) resolvePromise(newPromise, x, resolve, reject); } catch (err) { reject(err) }}复制代码
所以,promise的then方法是如何确定在前面resolve或者reject执行完拿到结果再执行的,分两种情况:
a = new Promise((resolve, reject) => { resolve(1)})复制代码
- 对a只调用了一次then方法:这里进入then方法,判断状态为pending,直接返回一个new Promise(),像存放resolve状态的数组内push一个函数f。在所有同步操作完成后,在eventLoop的末尾执行resolve内部的函数,从resolve状态的数组中取出函数f并执行。
- 对a被调用多次then方法:在第二次调用then方法时,前面的promise状态已经变为resolved或者reject,这样,进入then方法,判断状态为resolved或者reject,就setTimeout,将then内部的函数放在eventLoop的末尾。