【深入学习JS】14_promise

Promise

在事件循环中提到了promiseasync/await中提到了promise,于是来深入学习一下Promise

什么是Promise

promise是JS中进行异步编程的新的解决方案。在promise中,有三种状态pendingresolvedrejected。状态只能从pending变为resolved或者rejected,状态一旦发生改变,就不能再变了。不管是成功还是失败,最后都会返回一个Promise对象。

为什么使用Promise

  • promise的回调机制更灵活,你可以在异步任务之后再指定回调函数:

    // 使用纯回调函数的时候,需要再开启任务的时候同时指定成功和失败回调
    todoSomeThing(getList, successCallback, failCallBack);
    
    // 如果使用Promise的话
    const p = todoSomething(getList);
    p.then(successCallback, failCallBack);
    
  • 支持链式调用,可以解决回调地狱的问题;

    // 纯回调函数
    todoSomeThing(function(res){
        todoSomeThingElse(res, function(newRes){
            todoOtherThing(newRes, function(finalResult){
                console.log('Got the final result:', finalResult);
            }, failCallback);
        }, failCallback)
    }, failCallback);
    
    // 使用Promise的话可以使用链式调用解决这个问题
    todoSomeThing.then(res => {
        return todoSomeThingElse(res);
    }).then(res => {
        return todoOtherThing(res);
    }).then(res => {
        console.log('Got the final result:', res);
    }).catch(failCallback);
    
  • 如果觉得链式调用也不好用的话,可以结合async/await来解决:

    async function request(){
        try{
            const res1 = await todoSomeThing();
            const res2 = await todoSomeThingElse(res1);
            const res3 = await todoOtherThing(res2);
            console.log('Got the final result:', res3);
        } catch(err => {
            failCallback(err);
    	})
    }
    

Promise的API

// (resolve, reject) => {} 是执行器, 它会在会在 Promise 内部立即同步回调。
new Promise((resolve, reject) => {});    

// Promise.prototype.then里面接受两个参数,一个是为resolve时的成功回调,一个是reject时的错误回调,一般会省略不写,然后由catch接受
new Promise((resolve, reject) => {
    // ...
}).then(res => {}, err => {});

// Promise.prototype.catch里面接受一个错误回调函数,它是then的语法糖,相当于then(undefined, err => {});如果then里面有错误回调,且错误回调没有抛出异常,或者没有使用Promise.reject的话,不会走到catch
new Promise((resolve, reject) => {
    // ...
}).then(res => {
    // ...
}).catch(err => {});

new Promise.resolve();

// 返回一个失败的Promise对象
new Promise.reject();

// 传入多个Promise对象,返回一个新的Promise, 只有所有的Promise都成功才算成功,只要有一个失败了就 直接失败
new Promise.all([
    new Promise(res => {}, err => {}),
    new Promise(res => {}, err => {})
]);

// 传入多个Promise对象,返回一个新的Promise, 第一个完成的 promise 的结果状态就是最终的结果状态
new Promise.race([
    new Promise(res => {}, err => {}),
    new Promise(res => {}, err => {})
]);

// 传入多个Promise对象,返回一个新的Promise, 可以获得每个Promise的状态
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));
// 打印结果: "fulfilled" "rejected"

// 传入多个Promise对象,返回一个新的Promise, 当有一个promise成功就返回成功(resolve)状态,或者所有的 promises 都失败,那么返回的 promise 就会 异步地(当调用栈为空时) 变成成功/失败(resolved/reject)状态。
new Promise.any([
    new Promise(res => {}, err => {}),
    new Promise(res => {}, err => {})
]);

Promise常见问题

  • 改变Promise状态的方式: Promise.resolve、Promise.reject和 throw new Error

  • 一个 promise 指定多个成功/失败回调函数时,当 promise 改变为对应状态时都会调用;

  • promise.then返回的新Promise的结果有那些:

    • 如果抛出异常,新的promise变为reject

    • 如果返回的是非promise的任意值,新promise会变为resolve

    • 如果返回的另一个promise,这个promise会变为新的promise的结果。

      // 1. 在then中抛出异常,新的promise会变为reject
      new Promise((resolve, reject) => {
          resolve(1);
      }).then(res => {
          throw new Error(2);
      }).catch(err => {
          console.log(err);
      })
      
      // 2. 如果返回的是非promise的任意值,新promise会变为resolve
      new Promise((resolve, reject) => {
          resolve(1);
      }).then(res => {
          return 2;
      }).then(res => {
          console.log(res);    // 2
      })
      
      // 3. 如果返回的另一个promise,这个promise会变为新的promise的结果
      new Promise((resolve, reject) => {
          resolve(1);
      }).then(res => {
          return Promise.resolve(2);
      }).then(res => {
          console.log(res);          // 2
      })
      
  • 中断promise:

    new Promise((resolve, reject) => {
        return new Promise(() => {});      // 返回一个pendding状态的promise对象
    })
    

手写Promise

创建文件:

- index.js
- promise.js

第一步:

正常情况下我们是这么调用promise的:

// index.js
let t = new Promise((resolve, reject) => {
    resolve('Hello World');
})

t.then((res) => {
    console.log(res);
}, (err) => {
    console.log(err);
})

// 输入 Hello Wolrd

由此我们可以看出,Promise是一个构造函数或者类,且含有一个then方法。

// promise.js

// 先定义三个状态常量
const PENDING = 'pending';
const RESOLVED = 'resolved';     // 也可以标准些,写成fulfilled
const REJECTED = 'rejected';

class myPromise {
    status = PENDING;            // 记录当前的状态
	result = undefined;          // 状态为resolved时返回的结果
	reason = undefined;          // 状态为rejected时返回的结果
	
	constructor(excutor){
        const resolve = (result) => {    
            this.status = RESOLVED;
            this.result = result;
        }
        
        const reject = (reason) => {
            this.status = REJECTED;
            this.reason = reason;
        }
        
        excutor(resolve, reject);         // new Promise时是立即执行的,且需要两个参数
    }

	then(onResolved, onRejected){
        if(this.status === RESOLVED){       // 如果当前状态是`resolved`则调用onResolved
            onResolved(this.result);
        }
        
        if(this.status === REJECTED){      // / 如果当前状态是`rejected`则调用onRejected
            onRejected(this.reason);
        }
    }
}

module.exports = myPromise;

index.js中使用:

const MyPromise = require('./promise');

const t = new MyPromise((resolve, reject) => {
  resolve('Hello wolrd');
})

t.then((res => {
  console.log(res);
}))

// 输入 Hello Wolrd

第二步:

// 使用自定义的Promise
// 这时候是先输出Hello World 、 123
const MyPromise = require('./promise');

const t = new MyPromise((resolve, reject) => {
  resolve('Hello wolrd');
})

t.then((res => {
  console.log(res);
}))

console.log('123')
// 使用原生的Promise
// 这时候是先输出 123 Hello World
const t = new Promise((resolve, reject) => {
  resolve('Hello wolrd');
})

t.then((res => {
  console.log(res);
}))

console.log('123')

所以我们应该让then执行晚一些:

// myPromise 修改then中的方法,加入setTimeout
 then(onResolved, onRejected){
    if(this.status === RESOLVED){
      setTimeout(() => {
        onResolved(this.result);
      })
    }

    if(this.status === REJECTED){
      setTimeout(() => {
        onRejected(this.reason);
      })
    }
  }

第三步:

// 使用原生的Promise
// 输出 Hello World
const t = new Promise((resolve, reject) => {
  setTimout(() => {
  	resolve('Hello wolrd');
  });
})

t.then((res => {
  console.log(res);
}))

但是使用我们自定的myPromise并不会有任何输出,因为在调用then时执行了内部的setTimeout,但是会延迟执行,导致此时的statue = PENDING,所以在then中没有任何处理,所以我们要将在PENDING时将then的回调函数存起来,在改变状态的时候调用。

class MyPromise {
  status = PENDING;
  result = undefined;
  reason = undefined;
  onResolvedArr = [];       // 第一步:声明存放then中的resolve的变量
  onRejectArr = [];         // 声明存放then中的reject的变量

  constructor(excutor){
    const resolve = (result) => {
      if(this.status === PENDING){                   
        this.status = RESOLVED;
        this.result = result;
        this.onResolvedArr.map((fn) => fn());  // 第三步,在改变状态的同时,看看有无回调函数并调用。
      }
    };
    const reject = (reason) => {
      if(this.status === PENDING){
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectArr.map((fn) => fn()); // 第三步,在改变状态的同时,看看有无回调函数并调用。
      }
    };

    excutor(resolve, reject);
  }

  then(onResolved, onRejected){
    if(this.status === RESOLVED){
      setTimeout(() => {
        onResolved(this.result);
      })
    }

    if(this.status === REJECTED){
      setTimeout(() => {
        onRejected(this.reason);
      })
    }

    if(this.status === PENDING){             // 第二步: 在pending时,将这些回调函数存放起来
      this.onResolvedArr.push(() => {
        onResolved(this.result);
      });
      this.onRejectArr.push(() => {
        onRejected(this.reason);
      })
    }
  }
}

第四步:

// 原生Promise中的then是支持链式调用的
const t = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Hello wolrd');
  })
})

t.then((res => {
  return res;
})).then((res) => {
  console.log(res);       // Hello Wolrd
})

我们需要给我们的自定义Promise中的then也支持链式调用,所以内部应该返回一个new MyPromise

then(onResolved, onRejected){
    const newPromise = new MyPromise((resolve, reject) => {
      if(this.status === RESOLVED){
        setTimeout(() => {
          const result = onResolved(this.result);
          resolve(result);
        })
      }
  
      if(this.status === REJECTED){
        setTimeout(() => {
          const result = onRejected(this.reason);
          reject(result);
        })
      }
  
      if(this.status === PENDING){
        this.onResolvedArr.push(() => {
          const result = onResolved(this.result);
          resolve(result);
        });
        this.onRejectArr.push(() => {
          const result = onRejected(this.reason);
          reject(result);
        })
      }
    })
    
    return newPromise;
  }

第五步:

以上代码只满足链式调用中then是直接返回一个值的情况,如果是下面这种情况就不行了:

const t = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Hello wolrd');
  })
})

t.then((res => {
  return new Promise((resolve, reject) => {
    resolve('test');
  });
})).then((res) => {
  console.log(res);
})

修改then,使用handlePromise来处理:

then(onResolved, onRejected){
    const newPromise = new MyPromise((resolve, reject) => {
      if(this.status === RESOLVED){
        setTimeout(() => {
          const result = onResolved(this.result);
          handlePromise(result, newPromise, resolve, reject);
        })
      }
  
      if(this.status === REJECTED){
        setTimeout(() => {
          const result = onRejected(this.reason);
          handlePromise(result, newPromise, resolve, reject);
        })
      }
  
      if(this.status === PENDING){
        this.onResolvedArr.push(() => {
          const result = onResolved(this.result);
          handlePromise(result, newPromise, resolve, reject);
        });
        this.onRejectArr.push(() => {
          const result = onRejected(this.reason);
          handlePromise(result, newPromise, resolve, reject);
        })
      }
    })
    
    return newPromise;
  }
const handlePromise = (result, newPromise, resolve, reject) => {
  // result与newPromise不能相等,不然循环引用时会出现问题
  if(result === newPromise){
  	return reject(new TypeError('error'));
  }
  if((typeof result === 'object' && result !== null) || typeof result === 'function'){
	const then = result.then;
    if(typeof then === 'function'){
      // 如果then是一个函数,则递归调用handlePromise使得最后的值不是函数
      then.call(result, r => {
        handlePromise(r, newPromise, resolve, reject);
      }, e => {
        reject(e);
      })
    }else {
      resolve(result);
    }
  }else {
    // 如果是返回一个变量,则直接resolve
    resolve(result);
  }
}

第六步:

加上一些try catch确保容错:

// 定义三种状态
const RESOLVED = 'resolved';
const REJECTED = 'rejecte';
const PENDING = 'pending';

const handlePromise = (result, newPromise, resolve, reject) => {
  // 如果是自己就抛出异常
  if(result === newPromise){
    throw new Error('can not return oneself');
  }

  if((typeof result === 'object' && result !== null) || typeof result === 'function'){
    const then = result.then;
    if(typeof then === 'function'){
      try {
        then.call(result, r => {
          handlePromise(r, newPromise, resolve, reject);
        }, e => {
          reject(e);
        })
      } catch (error) {
        reject(error);
      }
    }else {
      resolve(result);
    }
  }else {
    resolve(result);
  }
}

class MyPromise {
  status = PENDING;
  result = undefined;
  reason = undefined;
  onResolvedArr = [];
  onRejectArr = [];

  constructor(excutor){
    const resolve = (result) => {
      if(this.status === PENDING){
        this.status = RESOLVED;
        this.result = result;
        this.onResolvedArr.map((fn) => fn());
      }
    };
    const reject = (reason) => {
      if(this.status === PENDING){
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectArr.map((fn) => fn());
      }
    };

    try { 
      excutor(resolve, reject);
    } catch (error) {
      reject(error);
    }
    
  }

  then(onResolved, onRejected){
    onResolved = typeof onResolved === 'function' ? onResolved : (data) => data;
    onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw new Error(err) };

    const newPromise = new MyPromise((resolve, reject) => {
      if(this.status === RESOLVED){
        setTimeout(() => {
          try { 
            const result = onResolved(this.result);
            handlePromise(result, newPromise, resolve, reject);
          } catch (error) {
            reject(error);
          }
        })
      }
  
      if(this.status === REJECTED){
        setTimeout(() => {
          try { 
            const result = onRejected(this.reason);
            handlePromise(result, newPromise, resolve, reject);
          } catch (error) {
            reject(error);
          }
        })
      }
  
      if(this.status === PENDING){
        this.onResolvedArr.push(() => {
          try { 
            const result = onResolved(this.result);
            handlePromise(result, newPromise, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
        this.onRejectArr.push(() => {
          try { 
            const result = onRejected(this.reason);
            handlePromise(result, newPromise, resolve, reject);
          } catch (error) {
            reject(error);
          }
        })
      }
    })
    
    return newPromise;
  }

  catch(onRejected){
    this.then(undefined, onRejected);
  }
}

module.exports = MyPromise;

Promise的基本实现就完成了。

参考


若有错误,欢迎指出,感谢~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值