# promise
# 关键点
- 1 promise状态只允许改变一次
- 2 发布订阅模式解决,异步场景改变resolve/reject状态,执行不到.then
- 3 链式回调问题,通过让.then返回一个全新的promise对象来解决链式回调
# 一 标志位只能改变一次
- new Promise 时候 让excutor执行,传如一个成功resolve方法,一个reject方法。
- 通过这两个方法改变,reason和val的值。和padding的状态。
- 关键点 relsove 和 reject的执行需要判断 当前是PENDING状态。
const PADDING = 'PADDING';
const RESOLVE = 'RESOLVE';
const REJECT = 'REJECT';
class promise{
constructor(executor){
this.status = PADDING;
this.val = '';
this.reason = '';
let resolve = (val)=>{
if(this.status === PADDING){
this.status = RESOLVE;
this.val = val
}
};
let reject = (reason)=>{
if(this.status === PADDING){
this.status = REJECT;
this.reason = reason
}
};
try {
executor(resolve,reject)// new时调
}catch (e) {
reject(e)
}
}
then(onFulfilled,onRejected){
//同步
if(this.status === RESOLVE){
onFulfilled(this.val)
}
if(this.status === REJECT){
onRejected(this.reason)
}
}
}
# 二 发布订阅
通过setTimeOut(function(){ resolve('success') })模拟异步,此时的then方法中status状态为 PENDING。如果.then中状态为PENDING,把成功和失败的会调函数fn都放在一个数组中,当执行reove/reject方法时,让数组中的fn批量执行。
class myPromise {
constructor(executor) {
this.status = PADDING;
this.val = undefined;//成功原因
this.reason = undefined;
this.onResolveCallbacks = [];//成功回调数组
this.onRejectcallbacks = [];//失败回调数组
//成功函数
let resolve = (val)=>{
if(this.status === PADDING){
this.val = val;
this.status = RESOLVE;
this.onResolveCallbacks.forEach( fn=>{ fn() })
}
};
//失败函数
let reject = (reason)=>{
if(this.status === PADDING){
this.reason = reason;
this.status = REJECT;
this.onRejectcallbacks.forEach( fn=>{ fn() })
}
};
try {
executor(resolve,reject); //new时候执行
}catch(error)
{
reject(error)
}
}
then(onFulfilled,onRejected){
//同步的情况
if(this.status === RESOLVE){
onFulfilled(this.val)
}
if(this.status === REJECT){
onFulfilled(this.reason)
}
console.log('----this.status-',this.status)
//异步的情况 利用发布订阅
if(this.status === PADDING){
//异步先订阅号数据
this.onResolveCallbacks.push(()=>{
onFulfilled(this.val)
});
console.log('------this.onResolveCallbacks',this.onResolveCallbacks)
this.onRejectcallbacks.push(()=>{
onRejected(this.reason)
});
}
}
}
# 三 链式调用
链式回调问题主要通过在.then中返回一个全新的Promise。 (必须是全新的promise,因为如果返回this,此时的promise状态已经被改变,所以不能继续使用)
.then返回的几种情况
- 1 返回一个成功/失败的promise对象,下一个then根据promise状态接收
- 2 返回一个普通值,下一个then根据成功的状态接收
- 3 如果抛出错误 下一个then根据失败的状态接收
- 根据上一个promise的返回结果x,如果x是一个promise调用then。
- 如果x是一个普通的值或者underfind则返回promise一个新的promise。
const PADDING = 'PADDING';
const RESOLVE = 'RESOLVE';
const REJECT = 'REJECT';
/**
* new Promise 时候 让excutor执行,传如一个成功resolve方法,一个reject方法。通过这两个方法改变,reason和val的值。和padding的状态。
* 状态发生变化后不能改变。
*
* promise 链式回调实现方式, 发布订阅。
*/
const resolvePromise = (promise2,x,resolve,reject)=>{
}
class myPromise {
constructor(executor) {
this.status = PADDING;
this.val = undefined;//成功原因
this.reason = undefined;
this.onResolveCallbacks = [];//成功回调数组
this.onRejectcallbacks = [];//失败回调数组
//成功函数
let resolve = (val)=>{
if(this.status === PADDING){
this.val = val;
this.status = RESOLVE;
this.onResolveCallbacks.forEach( fn=>{ fn() })
}
};
//失败函数
let reject = (reason)=>{
if(this.status === PADDING){
this.reason = reason;
this.status = REJECT;
this.onRejectcallbacks.forEach( fn=>{ fn() })
}
};
try {
executor(resolve,reject); //new时候执行
}catch(error)
{
reject(error)
}
}
/**
* 根据返回值判断then的处理逻辑
* 1 如果返回promise接着往下执行
* 2 如果返回成功的promise执行onFulfilled
* 3 如果返回失败的promise执行onRejected
* 4 根据上一次返回的值,判断promise2的状态
* @param onFulfilled
* @param onRejected
*/
then(onFulfilled,onRejected){
let promise2 = new Promise(((resolve, reject) => {
//同步的情况
if(this.status === RESOLVE){
/**
* 根据x的值 推导promise2的状态
* x可能是普通值
* x也可能是promise,接着调用then
*/
setTimeout(function () {
let x = onFulfilled(this.val);
resolvePromise(promise2,x,resolve,reject)
},0)
}
if(this.status === REJECT){
let x = onFulfilled(this.reason)
}
console.log('----this.status-',this.status)
//异步的情况 利用发布订阅
if(this.status === PADDING){
//异步先订阅号数据
this.onResolveCallbacks.push(()=>{
let x = onFulfilled(this.val)
});
console.log('------this.onResolveCallbacks',this.onResolveCallbacks)
this.onRejectcallbacks.push(()=>{
let x = onRejected(this.reason)
});
}
}));
}
}
module.exports = myPromise
# promise练习题
- then 正常返回 resolved,里面有报错则返回 rejected。
- catch 正常返回 resolved, 里面有报错则返回 rejected。
题目1
const p2 = Promise.resolve().then(()=>{
throw new Error('error') //会返回rejectred的promise,之后的回调是then
});
题目2
const p3 = Promise.reject('111').catch(()=>{
console.log('catch');// reject执行完调用catch,catch会返回一个成功的promise,之后可以调用 then
}).then(()=>{
console.log('111')//
})
题目3
//打印结果 1 2 3
Promise.resolve().then(()=>{
console.log(1);
throw new Error('error')
}).catch(()=>{
console.log(2); // catch之后会返回一个成功的promise
}).then(()=>{
console.log(3);
})
# async /await 和 Promise的关系
- 执行async函数,返回的是Promise对象
- await 相当于 Promise的then
- try...catch可以捕获异常,代替来Promise.catch
- await后面的内容都是异步的内容
//执行aysnc函数,返回的是Promise对象
async function fn1() {
return 100; //相当于return Promise.resolve(100);
}
const res = fn1();
res.then((val)=>{
console.log(val)
});
async function fn2(){
const data = await Promise.resolve(3000)
console.log(data) // 可以打印出300,await相当于 Promise.then的回调
}
async function fn2(){
const data = await 3000 //相当于Promise.resolve(3000)
console.log(data) // 可以打印出300,await相当于 Promise.then的回调
}
//try catch 相当于 Promise的catch
async function fn3){
try{
const res = await Promise.reject('error')
}catch{
console.log(res)
}
}
async function fn4{
const res = await Promise.reject('error');
console.log(res);// 此时的res不会被执行,因为await相当于Promise的then,当reject 时候会执行catch不会执行then,所以此时必须要通过try catch 捕获。
}
async function async1(){
console.log('fn start')
await async2()
//await后面当作异步代码来执行
console.log('fn end')
}
console.log('script start');
async1()
console.log('script end');
async function async2(){
console.log('async2')
}
打印顺序
script start
fn start
async2
script end
fn end
# 场景题分析
// 红灯三秒亮一次,绿灯一秒亮一次,黄灯2秒亮一次;如何让三个灯不断交替重复亮灯?(用Promise实现)
function red(){
console.log('red');
}
function green(){
console.log('green');
}
function yellow(){
console.log('yellow');
}
function run (callback,delay){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('run');
callback();
},delay)
})
}
function fn(){
Promise.resolve().then(()=>{
return run(red,3000)
}).then(()=>{
return run(green,2000)
}).then(()=>{
return run(yellow,1000)
}).then(()=>{
fn();
})
}
fn();
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
async function fn(){
return 100
}
(async function(){
const a = fn();//a的值为 Promise.resolve(100)
const b = await fn();// b的值为100,await是promise.resolve的回调
})()
(async function(){
console.log('start')
const a = await 100
console.log(a) // a的值为100
const b = await Promise.resolve(200)
console.log(b) // b的值为200
const c = await Promise.reject(300) //此时会报错因为c是 Promise.resolve()的回调,此时拿不到reject的值,需要使用try catch不然报错。执行不下去了
console.log(c)
})()
Promise.resolve().then(() => {
return new Error('error!!!')// 会返回一个成功的promise,携带这error
}).then((res) => {
console.log('then: ', res) // 打印 then: Error: error!!!
}).catch((err) => {
console.log('catch: ', err)
})
//变换
Promise.resolve().then(() => {
throw new Error('error!!!')//
}).then((res) => {
console.log('then: ', res)
}).catch((err) => {
console.log('catch: ', err) // 打印 catch: Error: error!!!
})
const promise = Promise.resolve().then(() => {
return promise
})
promise.catch(console.error)
// 执行结果如下:
// TypeError: Chaining cycle detected for promise #<Promise>
// .then或 .catch返回的值不能是 promise本身,否则会造成死循环。类似于:
process.nextTick(function tick () {
console.log('tick')
process.nextTick(tick)
})
# promise.all()使用
Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。
- 处理多个promise的状态,当p1,p2都成功时,返回的是 [p1,p2].
- 当p1,p2有一个失败时候,走的是catch 方法,返回的值是第一个reject的值。
let p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
let p3 = Promise.reject('失败')
Promise.all([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log('err',error)
})
# promise.all()实现
Promise.myAll = function (promiseArr){
return new Promise((resolve, reject) => {
let len = promiseArr.length;
let result = new Array(len);
let count = 0;
for (let i=0;i<promiseArr.length;i++){
Promise.resolve(promiseArr[i]).then((res)=>{
count ++;
result[i] = res;
if(len ===count){
return resolve(result)
}
}).catch((error)=>{
return reject(error)
})
}
})
}
Promise.myAll([promise1,promise2]).then((res)=>{
console.log(res)
}).catch((error)=>{
console.log(error)
})
# Promise.race()
Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
},1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.race([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 打开的是 'failed'
})
# Promise.race()实现
Promise.myRace = function (promiseArr){
let len = promiseArr.length;
return new Promise(function (resolve, reject){
for (let i=0;i<len;i++){
Promise.resolve(promiseArr[i]).then(function (){
return resolve(promiseArr[i])
}).catch(function (error){
return reject(promiseArr[i])
})
}
})
}
# promisify
把一个普通的函数包装成promisify对象
const fs = require('fs');
function promisify (original){
return function(...args){
return new Promise((resolve, reject) => {
// 将 arguments 里面新增一个 original 的 callback,用来改变 promise 的状态
args.push(function callback(err, ...values) {
console.log('args',err,...values)
if (err) {
return reject(err);
}
return resolve(...values)
});
console.log('---args',args)
// 执行原函数(args 已经新增了 callback 了)
original.call(this, ...args);
});
}
}
const readFile = promisify(fs.readFile)
readFile('./test.js').then(()=>{
console.log('fff')
})
← arguments 参数传递 ts 学习 →