Promise产生的背景
在前端常常需要处理各种各样的异步事件,当一个异步事件的结果是另一个异步事件的条件时,就不可避免地引起回调地狱。什么是回调地狱?如下就是。这样的代码形式极不方便维护,另外也不方便进行错误处理。由此产生了Promise。
$.ajax({
success:()=>{
$.ajax({
success:()=>{
$.ajax({
success:()=>{
Here is code...
}
})
}
})
}
})
Promise是什么
Promise是一个类(函数或者对象),因此通过new就可以创建一个promise。这里先简单说一下Promise的特点:
1.创建promise的时候接受一个参数,这个参数是一个函数,称为执行函数(executor),并且在创建时会立即执行。
2.每个promise都有三个状态:等待(pending)、成功(fulfilled)和失败(rejected)。
3.默认是等待态,创建时提供两个回调函数(resolve和reject)来修改promise的状态,resolve让promise变成成功态,reject让promise变成失败态,另外抛出错误也会让promise变成失败态。
4.每个promise实例都有then方法,then接受两个回调函数,分别时成功回调和失败回调
5.不管是成功还是失败,状态都只能改变一次,即只有在当前状态是等待状态时才可以改变状态。
Promise原理实现(基于Promise A+规范)
场景1:先看一种最简单的情况,执行函数直接返回成功或失败,然后调取then方法。
let promise = new Promise((resolve,reject)=>{
// resolve('value') //(1)
// reject('reason') //(2)
// throw new Error('error') //(3)
})
promise.then(()=>{
console.log('success');
},(err)=>{
console.log('fail');
})
/*此时实现起来比较简单,创建一个Promise类,在构造函数中直接调用执行函数,返回resolve和reject两个函数供用户调用,再给这个类创建一个then方法,把当前promise成功或失败的结果值返回给用户,并调用客户的回调函数。*/
//三种状态
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class Promise{
constructor(executor){
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
let resolve = (value) => {
if(this.status === PENDING) { //只有状态时等待时才可以更新
this.status = FULFILLED
this.value = value
}
}
let reject = (reason) => {
if(this.status === PENDING) {
this.status = REJECTED
this.reason = reason
}
}
try {
executor(resolve,reject); //直接调用执行函数
} catch (error) { //捕获当前异常 ,异常作为失败的原因
reject(error)
}
}
then(onFulfilled,onRejected){
if(this.status === FULFILLED){
onFulfilled(this.value)
}
if(this.status === REJECTED){
onRejected(this.reason)
}
}
}
场景2:直接执行函数是一个异步函数。
let promise = new Promise((resolve,reject)=>{
setTimeout(() => { //如果调用了resolve 就让储存的成功回调函数执行
resolve('value')
}, 1000);
})
promise.then(()=>{ //如果调用then的时候没有成功或者失败,就先保存两个回调
console.log('success');
},(err)=>{
console.log('fail',err);
})
/*由于涉及到异步函数,此时如果执行then函数,当前的状态是等待态,不执行任何函数,因此需要对then方法进行修改。如果当前状态是等待态,那么就把用户的回调函数储存起来(观察者模式),待状态改变时再把储存起来的回调函数执行 */
//三种状态
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class Promise{
constructor(executor){
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolveCallbacks = [] //存放成功时的回调
this.onRejectCallbacks = [] //存放失败时的回调
let resolve = (value) => {
if(this.status === PENDING) { //只有状态时等待时才可以更新
this.status = FULFILLED
this.value = value
this.onResolveCallbacks.forEach(fn=>fn()) //发布过程
}
}
let reject = (reason) => {
if(this.status === PENDING) {
this.status = REJECTED
this.reason = reason
this.onRejectCallbacks.forEach(fn=>fn())
}
}
try {
executor(resolve,reject); //直接调用执行函数
} catch (error) { //捕获当前异常 ,异常作为失败的原因
reject(error)
}
}
then(onFulfilled,onRejected){
if(this.status === FULFILLED){
onFulfilled(this.value)
}
if(this.status === REJECTED){
onRejected(this.reason)
}
if(this.status === PENDING){
this.onResolveCallbacks.push(()=>{
onFulfilled(this.value)
})
this.onRejectCallbacks.push(()=>{
onRejected(this.reason)
})
}
}
场景3:根据Promise A+规范,当返回的值是promise,就继续执行它的then方法;当返回普通值(包括undefined,即不写),返回的值作为下一次then的成功回调
let promise = new Promise((resolve,reject)=>{
resolve('hello');
})
let promise2 = promise.then((data)=>{
// return data //如何将data向下传递:调用promise2的resolve方法
return new Promise((resolve,reject)=>{
resolve(new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('hello');
}, 1000);
}));
})
// return promise2
})
promise2.then(data=>{
console.log('success:'+data)
},err=>{
console.log('err:'+err)
})
/*此时情况就变得有些复杂,我们必须先去判断第一个then方法的调用返回的是什么,根据返回结果的类型,再决定resolve或reject对应的值。我们这里先假设回调的是resolve。
*/
let x = onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)//根据回调函数的结果调用一个函数来决定返回值
//resolvePromise函数的实现
const resolvePromise = (promise2,x,resolve,reject )=>{
//判断 可能你的promise要和别人的promise来混用
//可能不同的promise库之间要相互调用
if(promise2 === x) {//如果x和promise2是同一个,则永远不会成功,直接抛出错误
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
//---判断x的状态 判断x是不是promise---
//先判断是不是对象(原生的是对象)或者函数(别人写的有可能是函数)
if((typeof x === 'object' && x!==null) || typeof x === 'function'){
let called; //为了考虑别人的promise不健壮 状态只能改变一次
try{
let then = x.then;//取出then方法 这个then方法采用defineProperty来定义的
if(typeof then === 'function'){
//判断then是不是一个函数,如果不是函数 说明不是promise
//只能是promise
then.call(x,y=>{//如果x是一个promise就采用这个promise的返回结果
if(called) return;
called = true
resolvePromise(promise2,y,resolve,reject)
},r=>{
if(called) return;
called = true
reject(r) //直接用r作为失败的结果
})
//为什么不用x.then(()=>{},()=>{}) 这样会再一次取then方法
}else {
resolve(x) //比如:x={then:123}
}
}catch(e){
if(called) return;
called = true
reject(e); //取then失败 直接触发promise2的失败逻辑
}
}else {
//肯定不是promise
resolve(x);// 是普通值 直接成功即可
}
}
场景4:catch和finally只是promise的两个语法糖,实现起来比较容易
catch(errCallback) {//没有成功的then 语法糖
return this.then(null,errCallback)
}
finally(Callback) {
Callback()
return this.then()
}
场景5:此外再实现Promise的all和race方法
//判断是否是promise
const isPromise = value =>{
if((typeof value === 'object' && value !== null) || typeof value === 'function'){
return typeof value.then === 'function'
}
return false;
}
//promise.all方法
Promise.all = function (promises) {
return new Promise((resolve,reject)=>{
let arr = [];//返回的数组
let i = 0;
let processData = (index,data)=>{
arr[index]=data;
if(++i === promises.length){
resolve(arr)
}
}
for(let i=0;i<promises.length;i++){
let current = promises[i];
if (isPromise(current)){
current.then(data=>{
processData(i,data)
},reject)
}else{
processData(i,current)
}
}
})
}
// Promise.race方法
Promise.race = function (promises) {
return new Promise((resolve,reject)=>{
let arr=[];
let firstIndex = true;
let firstBack;
let i=0;
let processData = (index,data)=>{
arr[index]=data;
if (firstIndex) {
firstBack = data
firstIndex = false
}
if(++i === promises.length){
resolve(firstBack)
}
}
for(let i=0;i<promises.length;i++){
let current = promises[i];
if(isPromise(current)){
current.then(data=>{
processData(i,data)
},reject)
}else{
processData(i,current)
}
}
})
}
Promise的完整实现
Promise的完整实现,全部代码
console.log('------------基于Promise A+规范的promise实现------------');
//宏 三种状态
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
const resolvePromise = (promise2,x,resolve,reject )=>{
//判断 可能你的promise要和别人的promise来混用
//可能不同的promise库之间要相互调用
if(promise2 === x) {//如果x和promise2是同一个,则永远不会成功,直接抛出错误
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
//---判断x的状态 判断x是不是promise---
//先判断是不是对象(原生的是对象)或者函数(别人写的有可能是函数)
if((typeof x === 'object' && x!==null) || typeof x === 'function'){
let called; //为了考虑别人的promise不健壮 状态只能改变一次
try{
let then = x.then;//取出then方法 这个then方法采用defineProperty来定义的
if(typeof then === 'function'){
//判断then是不是一个函数,如果不是函数 说明不是promise
//只能是promise
then.call(x,y=>{//如果x是一个promise就采用这个promise的返回结果
if(called) return;
called = true
resolvePromise(promise2,y,resolve,reject)
},r=>{
if(called) return;
called = true
reject(r) //直接用r作为失败的结果
})
//为什么不用x.then(()=>{},()=>{}) 这样会再一次取then方法
}else {
resolve(x) //比如:x={then:123}
}
}catch(e){
if(called) return;
called = true
reject(e); //取then失败 直接触发promise2的失败逻辑
}
}else {
//肯定不是promise
resolve(x);// 是普通值 直接成功即可
}
}
class Promise{
constructor(executor){
this.status = PENDING //默认等待
this.value = undefined
this.reason = undefined
this.onResolveCallbacks = [] //存放成功时的回调
this.onRejectCallbacks = [] //存放失败时的回调
let resolve = (value) =>{
if(value instanceof Promise){ //resolve一个promise的情况
return value.then(resolve,reject)
}
if(this.status === PENDING) { //只有状态是等待时才可以更新
this.status = FULFILLED
this.value = value
this.onResolveCallbacks.forEach(fn=>fn()) //发布过程
}
}
let reject = (reason) =>{
if(this.status === PENDING) {
this.status = REJECTED
this.reason = reason
this.onRejectCallbacks.forEach(fn=>fn())
}
}
//executor执行时传入两个参数 给用户改变状态
try { //try+catch只能捕获同步异常
executor(resolve,reject);
} catch (error) { //捕获当前异常 ,异常作为失败的原因
reject(error)
}
}
//只要x是普通值就会让下一个promise变成成功态
//这个x有可能是一个promise 采用这个promise的状态
then(onFulfilled,onRejected){
//穿透
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val
onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err}
//递归
let promise2 = new Promise((resolve,reject)=>{
if(this.status === FULFILLED){
setTimeout(() => {
try{
let x = onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}
if(this.status === REJECTED){
setTimeout(() => {
try{
let x = onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}
if(this.status === PENDING){
this.onResolveCallbacks.push(()=>{
setTimeout(() => {
try{
let x = onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
})
this.onRejectCallbacks.push(()=>{
setTimeout(() => {
try{
let x = onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
})
}
})
return promise2;
}
catch(errCallback) {//没有成功的then 语法糖
return this.then(null,errCallback)
}
finally(Callback) {
Callback()
return this.then()
}
}
Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
//判断是否是promise
const isPromise = value =>{
if((typeof value === 'object' && value !== null) || typeof value === 'function'){
return typeof value.then === 'function'
}
return false;
}
//手写promise.all方法
Promise.all = function (promises) {
return new Promise((resolve,reject)=>{
let arr = [];//返回的数组
let i = 0;
let processData = (index,data)=>{
arr[index]=data;
if(++i === promises.length){
resolve(arr)
}
}
for(let i=0;i<promises.length;i++){
let current = promises[i];
if (isPromise(current)){
current.then(data=>{
processData(i,data)
},reject)
}else{
processData(i,current)
}
}
})
}
// 手写Promise.race方法
Promise.race = function (promises) {
return new Promise((resolve,reject)=>{
let arr=[];
let firstIndex = true;
let firstBack;
let i=0;
let processData = (index,data)=>{
arr[index]=data;
if (firstIndex) {
firstBack = data
firstIndex = false
}
if(++i === promises.length){
resolve(firstBack)
}
}
for(let i=0;i<promises.length;i++){
let current = promises[i];
if(isPromise(current)){
current.then(data=>{
processData(i,data)
},reject)
}else{
processData(i,current)
}
}
})
}
module.exports = Promise
//npm install promises-aplus-tests -g
//promises-aplus-tests promise.js