Skip to content

promise

什么是promise

  • Promise 是一种在 JavaScript 中提供了异步编程范式的特殊对象。它可以被用来表示一个将来会发生的操作,或者说它是一个代表一个异步操作结果。使用它,可以轻松地实现异步请求,并用非常方便的方式处理请求成功或失败之后的操作

promise优缺点

  • 优:promise很好的解决了异步编程中的,回调层层嵌套问题,使代码结构看起来更加的简洁,可读性更强
  • 缺:promise一旦开始,无法取消或重置,意味着犯错了无法挽回

promiseA+规范

1、promise执行器

  • promise是一个类,当在实例化时,会立马执行构造器,构造器接受resolve, reject函数等待用户执行

WARNING

构造器在执行的时候如果出现异常直接会调用reject函数

js
class Promise {
  constructor ( executor ) {
    const resolve = ( value ) => {
      console.log( value )
    }
    const reject = ( reason ) => {
      console.log( reason )
    }
    try {
      // 出现异常直接走reject
      executor( resolve, reject )
    } catch ( e ) {
      reject( e )
    }
  }
}

new Promise( ( resolve, reject ) => {
  resolve( 'ok' )
} )

new Promise( ( resolve, reject ) => {
  throw 'error'
  resolve( 'ok' )
} )

2、promise状态

  • promise有种状态分别是pending(等待)fufilled(成功)rejected(失败)
  • 状态只能从pending=>fufilled或者pending=>rejected不能逆转
  • 当在执行构造器中的resolve或者reject时,如果此时的状态是pending,改变当前的状态,并且把成功或者失败的回调值存储在实例上
js
const STATUS = {
  PENDING: "PENDING",
  FUFILLED: "FUFILLED",
  REJECTED: "REJECTED",
};

class Promise {
  constructor ( executor ) {
    this.status = STATUS.PENDING
    this.value = undefined;
    this.reason = undefined;
    const resolve = ( value ) => {
      if ( this.status === STATUS.PENDING ) {
        this.status = STATUS.FUFILLED
        this.value = value
      }

    }
    const reject = ( reason ) => {
      if ( this.status === STATUS.PENDING ) {
        this.status = STATUS.REJECTED
       this.reason = reason
      }


    }
    try {
      executor( resolve, reject )
    } catch ( e ) {
      reject( e )
    }
  }
}

new Promise( ( resolve, reject ) => {
  resolve( 'ok' )
} )


new Promise( ( resolve, reject ) => {
  reject( 'error' )
} )

3、then函数

  • 1、then 接收2个回调函数(onFulfilled,onRejected),一个成功的回调,一个失败的回调
  • 2、当then回调传递的不是函数时,为了能够把值传递下去,会转换成函数
  • 3、当在调用then函数时,如果此次时的状态是pending(等待),会把成功、失败的回调加入到队列中,等待执行
  • 4、如果此时的状态不是pending(等待),会直接调用成功或者失败的回调
js
const STATUS = {
  PENDING: "PENDING",
  FUFILLED: "FUFILLED",
  REJECTED: "REJECTED",
};

class Promise {
  constructor ( executor ) {
    this.status = STATUS.PENDING
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
    const resolve = ( value ) => {
      if ( this.status === STATUS.PENDING ) {
        this.status = STATUS.FUFILLED
        this.value = value
        this.onFulfilledCallbacks.forEach( onRejected => {
          onRejected( this.value )
        } )
      }

    }
    const reject = ( reason ) => {
      if ( this.status === STATUS.PENDING ) {
        this.status = STATUS.REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach( onRejected => {
          onRejected( this.value )
        } )
      }


    }
    try {
      executor( resolve, reject )
    } catch ( e ) {
      reject( e )
    }

  }
  then ( onFulfilled, onRejected ) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : ( value ) => value
    onRejected = typeof onRejected === 'function' ? onRejected : ( err ) => { throw err };
    if ( this.status === STATUS.PENDING ) {
      this.onFulfilledCallbacks.push( () => onFulfilled )
      this.onRejectedCallbacks.push( () => onRejected )
    } else if ( this.status === STATUS.FUFILLED ) {
      onFulfilled( this.value )
    } else {
      onRejected( this.reason )
    }
  }
}

let promise = new Promise( ( resolve, reject ) => {
  resolve( 'ok' )
} )
promise.then((value)=>{
  console.log(value)
})


new Promise( ( resolve, reject ) => {
  reject( 'error' )
} )

4、then函数链式调用

  • 为了支持then能够链式调用,then函数会返回一个新的promise2
js
  then ( onFulfilled, onRejected ) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : ( value ) => value
    onRejected = typeof onRejected === 'function' ? onRejected : ( err ) => { throw err };
    
    const promise2 = new Promise( ( resolve, reject ) => {
      if ( this.status === STATUS.PENDING ) {
        this.onFulfilledCallbacks.push( () => onFulfilled )
        this.onRejectedCallbacks.push( () => onRejected )
      } else if ( this.status === STATUS.FUFILLED ) {
        onFulfilled( this.value )
      } else {
        onRejected( this.reason )
      }
    } )
    return promise2
 
  }

5、then函数回调返回值处理

  • 如果then回调函数调用出现异常,会走到下一个失败的then回调中
  • 如果then回调返回的是一个promise,promise成功或者失败,会走到下一个成功或者失败的then回调中
  • 如果then回调返回的是一个普通的值,不管是成功或者失败的回调,都有走到下一个成功的then回调中

循环引用案例

js
// 循环引用案例:
const promise2 = new Promise( ( resolve, reject ) => {
  resolve( 'ok' )
} )
// 循环引用,造成无限死循环需要处理成 reject失败
promise2.then(()=>{
  return promise2
})

// 处理异常
if ( promise2 === x ) {
  return reject( new TypeError( "循环引用,造成无限死循环" ) );
}
// ....

then回调返回值是对象,取then异常处理

js
const x = {}

Object.defineProperty( x, 'then', {
  get () {
    throw new Error( 'error' )
  }
} )

// 处理异常
try {
    const then = x.then
  } catch (e) {
      // 取then错误走下一个失败的then回调
    reject(e)
  }
// ....

如果then回调返回值是一个Promise,并且resolve中的值也是一个Promise,需要递归把值传递下去

js
const promise = new Promise( ( resolve, reject ) => {
  resolve( 'ok' )
} )
promise.then(()=>{
  return new Promise( ( resolve, reject ) => {
    resolve( Promise( ( resolve, reject ) => {
      resolve( 'ok' )
    } ) )
  } )
})

// 处理递归
then.call(x,(y)=>{
  // y有可能还是一个promise,递归到普通值结束
  resolvePromise(y, promise2, resolve, reject);
},(r)=>{
  // promise失败直接到下一个失败的then回调
  reject(r)
})
// ....

如果then回调返回值是不一个规范的Promise,即可以成功也可以失败,我们需要防止这种情况,不可以逆向

js
const promise = new Promise( ( resolve, reject ) => {
  resolve( 'ok' )
} )
promise.then(()=>{
  // 自己实现的Promise即可以成功也可以失败
  return new MyPromise( ( resolve, reject ) => {
    resolve('ok')
    reject('err')
  } )
})


// 处理不规范的Promise(即可以成功也可以失败)

let called; 
try {
  const then = x.then
  if(typeof then ==='function'){
    // 执行promise.then
    then.call(x,(y)=>{
      if (called) return;
      called = true;
      // y有可能还是一个promise,递归到普通值结束
      resolvePromise(y, promise2, resolve, reject);
    },(r)=>{
      if (called) return;
      called = true;
      // promise失败直接到下一个失败的then回调
      reject(r)
    })
  }else{
    if (called) return;
    called = true;
      // 普通值走下一个成功的then回调
    resolve(x)
  }
  
} catch (e) {
  if (called) return;
  called = true;
    // 取then错误走下一个失败的then回调
  reject(e)
}
// ....

6、完整源码实现

js
const STATUS = {
  PENDING: "PENDING",
  FUFILLED: "FUFILLED",
  REJECTED: "REJECTED",
};

/**
 * 
 * @param {*} x then回调的返回值
 * @param {*} promise promise2的实例
 * @param {*} resolve promise成功
 * @param {*} reject promise失败
 */

function resolvePromise ( x, promise2, resolve, reject ) {
  if ( promise2 === x ) {
    return reject( new TypeError( "循环引用,造成无限死循环" ) );
  }
  // 对象或者function都有可能是Promise
  if ( (typeof x === 'object' && x !== null) || typeof x ==='function') {
     // 防止别人实现的promise即可以成功,也可以失败
     let called; 
    try {
      const then = x.then
      if(typeof then ==='function'){
        // 执行promise.then
        then.call(x,(y)=>{
          if (called) return;
          called = true;
          // y有可能还是一个promise,递归到普通值结束
          resolvePromise(y, promise2, resolve, reject);
        },(r)=>{
          if (called) return;
          called = true;
          // promise失败直接到下一个失败的then回调
          reject(r)
        })
      }else{
        if (called) return;
        called = true;
         // 普通值走下一个成功的then回调
        resolve(x)
      }
      
    } catch (e) {
      if (called) return;
      called = true;
       // 取then错误走下一个失败的then回调
      reject(e)
    }

  } else {
    // 普通值走下一个成功的then回调
    resolve( x )
  }

}

class Promise {
  constructor ( executor ) {
    this.status = STATUS.PENDING
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
    const resolve = ( value ) => {
      if ( this.status === STATUS.PENDING ) {
        this.status = STATUS.FUFILLED
        this.value = value
        this.onFulfilledCallbacks.forEach( onRejected => {
          onRejected( value )
        } )
      }

    }
    const reject = ( reason ) => {
      if ( this.status === STATUS.PENDING ) {
        this.status = STATUS.REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach( onRejected => {
          onRejected( reason )
        } )
      }


    }
    try {
      executor( resolve, reject )
    } catch ( e ) {
      reject( e )
    }

  }
  
  then ( onFulfilled, onRejected ) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : ( value ) => value
    onRejected = typeof onRejected === 'function' ? onRejected : ( err ) => { throw err };

    const promise2 = new Promise( ( resolve, reject ) => {
      if ( this.status === STATUS.PENDING ) {
        this.onFulfilledCallbacks.push( () => {
          // 保证promise2实例化完成
          setTimeout( () => {
            try {
              const x = onFulfilled( this.value )
              // 处理回调的结果
              resolvePromise( x, promise2, resolve, reject )
            } catch ( e ) {
              // then回调出新异常直接调用下一个失败的then
              reject( e )
            }

          } )

        } )
        this.onRejectedCallbacks.push( () => {
          setTimeout( () => {
            try {
              const x = onRejected( this.reason )
              resolvePromise( x, promise2, resolve, reject )
            } catch ( e ) {
              reject( e )
            }

          } )

        } )
      } else if ( this.status === STATUS.FUFILLED ) {
        setTimeout( () => {
          try {
            const x = onFulfilled( this.value )
            resolvePromise( x, promise2, resolve, reject )
          } catch ( e ) {
            reject( e )
          }

        } )

      } else {
        setTimeout( () => {
          try {
            const x = onRejected( this.reason )
            resolvePromise( x, promise2, resolve, reject )
          } catch ( e ) {
            reject( e )
          }
        } )

      }
    } )
    return promise2

  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  static resolve(val) {
    return new Promise((resolve) => {
      resolve(val);
    });
  }

  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  }

  static all(promisArr) {
    return new Promise((resolve, reject) => {
      let result = [],
        count = 0;

      function done(i, data) {
        result[i] = data;

        if (count++ == i) {
          resolve(result);
        }
      }
      promisArr.map((p, i) => {
        p.then((data) => done(i, data), reject);
      });
    });
  }

  static race(promisArr) {
    return new Promise((resolve, reject) => {
      promisArr.map((p) => {
        p.then((data) => resolve(data), reject);
      });
    });
  }
}



Promise.defer = Promise.deferred = function () {
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};
module.exports = Promise;

6、case

  • 安装npm install promises-aplus-tests -g
  • 执行promises-aplus-tests promise.js(promise入口文件路径)

promise-case

then 交替执行

  • 如果有多个 fulfilled 状态的 promise 实例,同时执行 then 链式调用,then 会交替调用
  • 这是编译器的优化,防止一个 promise 持续占据事件
js
Promise.resolve().then(() => {
    console.log(1)
}).then(() => {
    console.log(2)
}).then(() => {
    console.log(3)
}).then(() => {
    console.log(4)
})

Promise.resolve().then(() => {
    console.log(10)
}).then(() => {
    console.log(20)
}).then(() => {
    console.log(30)
}).then(() => {
    console.log(40)
})

Promise.resolve().then(() => {
    console.log(100)
}).then(() => {
    console.log(200)
}).then(() => {
    console.log(300)
}).then(() => {
    console.log(400)
})
// 1 10 100 2 20 200 3 30 300 4 40 400

then返回promise

js
Promise.resolve().then(() => {
    console.log(0)
    return Promise.resolve(4) // 相当于多处一个 promise 实例,会慢2拍
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1)
}).then(() => {
    console.log(2)
}).then(() => {
    console.log(3)
}).then(() => {
    console.log(5)
}).then(() =>{
    console.log(6)
})
// 0 1 2 3 4 5 6