实现一个Promise,来学习一下Promise的源码思想。
Promise有三种状态,分别是pending,fulfilled,rejected。默认是pending状态,当调用resolve或reject时改变状态。
状态可以从pending变成resolve或者reject,一旦改变就无法在改变回去了 。
基本用法
let p = new MyPromise((resolve,reject)=>{
resolve('成功')
})
p.then(value=>console.log(value),err=>console.log(err))
首先需要声明一个构造函数,构造函数接受一个函数作为参数,该参数会立即执行。
function MyPromise(executor){
executor()
}
executor接受两个参数resolve,reject
function MyPromise(executor){
function resolve(){}
function reject(){}
try{
executor(resolve,reject)
}catch(e){
reject(e)
}
}
我们还需要一个变量来记录Promise的状态,调用resolve,reject时改变promise状态。
因为Promise的状态只能在pending状态改变,状态在resolve或reject状态都是不能改变的,所以需要判断。
function MyPromise(executor){
let self = this
self.status = 'pending'
function resolve(){
if(self.status === 'pending'){
self.status = 'resolve'
}
}
function reject(){
if(self.status === 'pending'){
self.status = 'reject'
}
}
try{
executor(resolve,reject)
}catch(e){
reject(e)
}
}
在调用resolve或reject时会传递一个值,并录下来。以便在then的回调中使用。
function MyPromise(executor){
let self = this
self.status = 'pending'
self.value
self.reason
function resolve(value){
if(self.status === 'pending'){
self.status = 'resolve'
self.value = value
}
}
function reject(reason){
if(self.status === 'pending'){
self.status = 'reject'
self.reason= reason
}
}
try{
executor(resolve,reject)
}catch(e){
reject(e)
}
}
Promise有一个then方法,then接受两个函数作为参数,分别是onFulfilled,onFailed。
第一个是onFulfilled,成功回调。
第二个是onFailed,失败回调。
onFulfilled,onFailed被调用时会将resolve或reject执行时保存的变量传递过去。
MyPromise.prototype.then = function(onFulfilled,onFailed){
let self = this
if(self.status ==='resolve') {
onFulfilled(self.value)
}
if(self.status ==='reject') {
onFailed(self.reason)
}
}
但是上面的实现是不支持异步的
let p = new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('成功')
},2000)
})
p.then(value=>console.log(value),err=>console.log(err))
上面的代码想着会打印出来成功,但实际上是不会打印出任何东西的。
因为在执行到then的时候还没有执行resolve,这意味着Promise的状态还没有改变,没改变的话,执行then函数,是什么都不会做的。等到2秒后resolve执行了,状态改了,但是then已经执行过了一次了。
所以需要继续完善代码。
function MyPromise(executor){
let self = this
self.status = 'pending'
self.value
self.reason
//声明两个数组来记住then传过来的函数
self.onResolvedCallBacks = []
self.onRejectedCallBacks = []
function resolve(value){
if(self.status === 'pending'){
self.status = 'resolve'
self.value = value
self.onResolvedCallBacks.forEach(function(fn){
fn()
})
}
}
function reject(reason){
if(self.status === 'pending'){
self.status = 'reject'
self.reason= reason
self.onRejectedCallBacks.forEach(function(fn){
fn()
})
}
}
try{
executor(resolve,reject)
}catch(e){
reject(e)
}
}
MyPromise.prototype.then = function(onFulfilled,onFailed){
let self = this
if(self.status ==='resolve') {
onFulfilled(self.value)
}
if(self.status ==='reject') {
onFailed(self.reason)
}
//如果执行到then,状态为pending,把传来的两个函数保存起来,等到状态改变在调用
if(self.status ==='pending'){
self.onResolvedCallBacks.push(function(){
onFulfilled(self.value)
})
self.onRejectedCallBacks.push(function(){
onFailed(self.reason)
})
}
}
再次测试,会在1秒之后打印出成功1,2秒后打印出成功2
let p1 = new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('成功1')
},1000)
})
let p2 = new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('成功2')
},2000)
})
p1.then(value=>console.log(value),err=>console.log(err))
p2.then(value=>console.log(value),err=>console.log(err))
链式调用
Promise是支持链式调用的,调用then后会返回一个新的Promise。
MyPromise.prototype.then = function(onFulfilled,onFailed){
let self = this
let promise2 = new MyPromise(function(resolve,reject){
if(self.status ==='resolve') {
onFulfilled(self.value)
}
if(self.status ==='reject') {
onFailed(self.reason)
}
//如果执行到then,状态为pending,把传来的两个函数保存起来,等到状态改变在调用
if(self.status ==='pending'){
self.onResolvedCallBacks.push(function(){
onFulfilled(self.value)
})
self.onRejectedCallBacks.push(function(){
onFailed(self.reason)
})
}
})
return promise2
}
接下来我们需要拿到then的onFulfilled和onFailed返回的值
如果返回的是普通值,直接调用promise2的resolve,该值作为resolve的参数。这样下一个then就可以获取到该值
如果是Promise则调用这个Promise.
下面声明一个新函数resolvePromise来进行判断
function resolvePromise(promise2,x,resolve,reject){
if((typeof x !==null && typeof x ==='object') || typeof x ==='function'){
//如果then为function类型,则认为返回了一个Promise
try{
let then = x.then
if(typeof then ==='function'){
//返回promise处理逻辑
}else{
//普通对象
resolve(x)
}
}catch(e){
reject(e)
}
}else{
//普通值
resolve(x)
}
}
如果promise2 和 x 引用了同一个对象,需要抛出一个错误
function resolvePromise(promise2,x,resolve,reject){
//避免返回自己
if(promise2 === x){throw new Error('循环引用了)}
if((typeof x !==null && typeof x ==='object') || typeof x ==='function'){
//如果返回值then为function类型,则认为返回了一个Promise
try{
let then = x.then
if(typeof then ==='function'){
//返回promise处理逻辑
}else{
//普通对象
resolve(x)
}
}catch(e){
reject(e)
}
}else{
//普通值
resolve(x)
}
}
如果返回的是Promise的话,参照Promise/A+规范编写逻辑
//2.3.3.1 let then = x.then.
//2.3.3.2 如果 x.then 这步出错,那么 reject promise with e as the reason..
//2.3.3.3 如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
// 2.3.3.3.1 resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
// 2.3.3.3.2 rejectPromise 的 入参是 r, reject promise with r.
// 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
// 2.3.3.3.4 如果调用then抛出异常e
// 2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略
// 2.3.3.3.4.3 否则,reject promise with e as the reason
上面规范翻译取自https://github.com/YvetteLau/Blog/issues/2
function resolvePromise(promise2,x,resolve,reject){
//避免返回自己
if(promise2 === x){throw new Error('循环引用了)}
if((typeof x !==null && typeof x ==='object') || typeof x ==='function'){
//如果返回值then为function类型,则认为返回了一个Promise
try{
let then = x.then
if(typeof then ==='function'){
//返回promise处理逻辑
then.call(x,function(y){
//这里不能直接resolve(y),因为有可能会有嵌套promise的情况,需要用递归来处理
resolvePromise(promise2,y,resolve,reject)
},function(r){
reject(r)
})
}else{
//普通对象
resolve(x)
}
}catch(e){
reject(e)
}
}else{
//普通值
resolve(x)
}
}
根据规范我们需要确保如果resolve和reject都调用了,优先调用第一个,剩下的忽略。所以需要用一个标志来判断
function resolvePromise(promise2,x,resolve,reject){
//避免返回自己
if(promise2 === x){throw new Error('循环引用了')}
if((typeof x !==null && typeof x ==='object') || typeof x ==='function'){
let called
//如果返回值then为function类型,则认为返回了一个Promise
try{
let then = x.then
if(typeof then ==='function'){
//返回promise处理逻辑
then.call(x,function(y){
//这里不能直接resolve(y),因为有可能会有嵌套promise的情况,需要用递归来处理'
if(called){return}
called = true
resolvePromise(promise2,y,resolve,reject)
},function(r){
if(called){return}
called = true
reject(r)
})
}else{
//普通对象
if(called){return}
called = true
resolve(x)
}
}catch(e){
if(called){return}
called = true
reject(e)
}
}else{
//普通值
resolve(x)
}
}
假如没有传递then的参数
p.then().then().then(res=>{console.log(res)})
根据规定
2.2.7.3 如果 onFulfilled 不是一个函数,promise2 以promise1的值fulfilled
2.2.7.4 如果 onRejected 不是一个函数,promise2 以promise1的reason rejected
MyPromise.prototype.then = function(onFulfilled,onFailed){
//PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
onFulfilled = typeof onFulfilled ==='function' ? onFulfilled : function(value) {return value}
onFailed = typeof onFailed==='function' ? onFailed: function(reason) {throw reason}
let self = this
let promise2 = new MyPromise(function(resolve,reject){
if(self.status ==='resolve') {
onFulfilled(self.value)
}
if(self.status ==='reject') {
onFailed(self.reason)
}
//如果执行到then,状态为pending,把传来的两个函数保存起来,等到状态改变在调用
if(self.status ==='pending'){
self.onResolvedCallBacks.push(function(){
onFulfilled(self.value)
})
self.onRejectedCallBacks.push(function(){
onFailed(self.reason)
})
}
})
return promise2
}
接下来我们在then中调用写好的resolvePromise方法
MyPromise.prototype.then = function(onFulfilled,onFailed){
//PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
onFulfilled = typeof onFulfilled ==='function' ? onFulfilled : function(value) {return value}
onFailed = typeof onFailed==='function' ? onFailed: function(reason) {throw reason}
let self = this
var promise2 = new MyPromise(function(resolve,reject){
if(self.status ==='resolve'){
//规范规定onFulfilled和onFailed需要是微任务,这里使用宏任务来模拟,原生Promise的实现并不是这样的。
setTimeout(function(){
try{
const x = onFulfilled(self.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
},0)
}
if(self.status ==='reject'){
setTimeout(function(){
try{
const x = onFailed(self.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
},0)
}
if(self.status ==='pending'){
self.onResolvedCallBacks.push(function(){
setTimeout(function(){
try{
const x = onFulfilled(self.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
},0)
})
self.onRejectedCallBacks.push(function(){
setTimeout(function(){
try{
const x = onFailed(self.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
},0)
})
}
})
return promise2
}
至此一个Promise已经实现了。全部代码如下
function MyPromise(executor){
let self = this
self.status = 'pending'
self.value
self.reason
//声明两个数组来记住then传过来的函数
self.onResolvedCallBacks = []
self.onRejectedCallBacks = []
function resolve(value){
if(self.status === 'pending'){
self.status = 'resolve'
self.value = value
self.onResolvedCallBacks.forEach(function(fn){
fn()
})
}
}
function reject(reason){
if(self.status === 'pending'){
self.status = 'reject'
self.reason= reason
self.onRejectedCallBacks.forEach(function(fn){
fn()
})
}
}
try{
executor(resolve,reject)
}catch(e){
reject(e)
}
}
function resolvePromise(promise2,x,resolve,reject){
//避免返回自己
if(promise2 === x){throw new Error('循环引用了')}
if((typeof x !==null && typeof x ==='object') || typeof x ==='function'){
let called
//如果返回值then为function类型,则认为返回了一个Promise
try{
let then = x.then
if(typeof then ==='function'){
//返回promise处理逻辑
then.call(x,function(y){
//这里不能直接resolve(y),因为有可能会有嵌套promise的情况,需要用递归来处理'
if(called){return}
called = true
resolvePromise(promise2,y,resolve,reject)
},function(r){
if(called){return}
called = true
reject(r)
})
}else{
//普通对象
if(called){return}
called = true
resolve(x)
}
}catch(e){
if(called){return}
called = true
reject(e)
}
}else{
//普通值
resolve(x)
}
}
MyPromise.prototype.then = function(onFulfilled,onFailed){
//PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
onFulfilled = typeof onFulfilled ==='function' ? onFulfilled : function(value) {return value}
onFailed = typeof onFailed==='function' ? onFailed: function(reason) {throw reason}
let self = this
var promise2 = new MyPromise(function(resolve,reject){
if(self.status ==='resolve'){
//规范规定onFulfilled和onFailed需要是微任务,这里使用宏任务来模拟,原生Promise的实现并不是这样的。
setTimeout(function(){
try{
const x = onFulfilled(self.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
},0)
}
if(self.status ==='reject'){
setTimeout(function(){
try{
const x = onFailed(self.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
},0)
}
if(self.status ==='pending'){
self.onResolvedCallBacks.push(function(){
setTimeout(function(){
try{
const x = onFulfilled(self.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
},0)
})
self.onRejectedCallBacks.push(function(){
setTimeout(function(){
try{
const x = onFailed(self.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
},0)
})
}
})
return promise2
}
Promise.prototype.catch
相当于调用then,但是只传递reject。
MyPromise.prototype.catch = function(onRejected){
return this.then(function(null,onRejected))
}
Promise.resolve
Promise.resolve(value)
返回一个以给定值解析后的Promise对象。
如果value是promise则返回这个promise
如果是thenable对象,返回的promise采用这个thenable的最终状态
如果是其他情况则返回成功状态。
MyPromise.resolve = function(value){
if(value instanceof Promise){return value}
return new MyPromise(function(resolve,reject){
if(value && value.then && typeof value.then === 'function'){
value.then(resolve,reject)
}else{
resolve(value)
}
})
}
Promise.reject
Promise.reject(reason)
返回一个reject状态的promise。
MyPromise.reject = function(reason){
return new MyPromise(function(resolve,reject){
reject(reason)
})
}
promise.all
一次传入多个异步任务,全部成功后返回一个数组,数组结果的顺序和传入的相同。如果有一个失败则失败。
MyPromise.all = function(promises){
if(!Array.isArray(promises)){throw TypeError('type error of params')}
return new MyPromise((resolve,reject)=>{
let resultArr = []
promises.forEach((p,index)=>{
MyPromise.resolve(p).then(res=>{
resultArr[index] = res
if(resultArr.length === promises.length){
console.log('here'+resultArr+promises.length)
resolve(resultArr)
}
},err=>reject(err))
})
})
}
promise.race
MyPromise.race = function(promises){
return new MyPromise((resolve,reject)=>{
promises.forEach((p,index)=>{
//有可能不是promise所以先用resolve解析一下
MyPromise.resolve(p).then(resolve,reject)
})
})
}