Promise

发布于 2022-12-23  28 次阅读


是什么

异步编程的一种解决方案

Promise 状态

  • 状态不受外界影响
    • pending 进行中
    • resolve 已成功
    • reject 已失败
  • 状态不可逆
    • promise 固化以后,再对promise对象添加回调,是可以直接拿到这个结果的,如果是事件的话,一旦错过了就错过了
var promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
  	Math.random() * 100 > 60 ? resolve('ok') : reject('no')
  })
})
promise.then(val => {
	console.log(val);
}, reason => {
	console.log(reason);
})

Promise 特性

then 的返回值作为下一次 then 的执行参数

var promise = new Promise(function(resolve, reject){
  setTimeout(function(){
  	Math.random() * 100 > 60 ? resolve('ok') : reject('no')
  })
})
promise.then(val => {
	console.log(val);
  return 1
}, reason => {
	console.log(reason);
  return 2
}).then(val => {
	console.log('then2-', val);
}, reason => {
	console.log('then2-', reason);
})
var promise = new Promise(function(resolve, reject) {
  setInterval(function() {
  	Math.random() * 100 > 60 ? resolve('ok') : reject('no')
  })
})
promise.then(val => {
	console.log(val);
  return new Promise((resolve, reject) => {
  	resolve('new promise')
  })
}, reason => {
	console.log(reason);
  return 2
}).then(val => {
	console.log('then2-', val);
}, reason => {
	console.log('then2-', reason);
})

resolve 导致抛出错误会被 reject 接收

var promise = new Promise((resolve, reject) => {
	resolve(a);
})

promise.then((val) => {
	console.log('resolve', val);
}, (reason) => {
	console.log('reject', reason);
})

catch 等同于:then 第一个参数为 null ,第二个参数为 reject 的回调函数

var promise = new Promise((resolve, reject) => {
	resolve(a);
})

promise.then(null, (reason) => {
	console.log('reject', reason);
})

promise.catch((reason) => {
	console.log('reject', reason);
})

pormise 状态一旦固化,就无法再改变

var promise = new Promise((resolve, reject) =>{
	resolve('ok');
  console.log(a); // 这个报错因为状态固化,无法被捕获
})

promise.then((val)=>{
	console.log('res', val);
}).catch((reason)=>{
	console.log('reject', reason);
})

catch 冒泡(可以捕获多层then的异常),then() 不传参数会被直接忽略

var promise = new Promise((resolve, reject) => {
  resolve(1);
});

promise
  .then((val) => {
    console.log("res", val);
    throw new Error("then error");
  })
  .then()
  .then()
  .catch((reason) => {
    console.log("reject", reason);
  });

值穿透

值穿透指的是,链式调用的参数不是函数时,会发生值穿透,就传入的非函数值忽略,传入的是之前的函数参数。

Promise.resolve(1)
    .then(2)
    .then(Promise.resolve(3))
    .then(console.log)

// 答案:

// 传入2或者promise的fulfilled状态都会发生值穿透。
// 打印 1
Promise.resolve(1)
  .then(2)
  .then(Promise.reject(3))
  .then(
    console.log
  , console.log
  );

// 答案:

// 打印 1 然后报错
Promise {<fulfilled>: undefined}
VM384:3 Uncaught (in promise) 3
Promise.resolve(1)
    .then(() => { return 2 })
    .then(() => { return 3 })
    .then(console.log)

// 答案

// 3
Promise.resolve(1)
    .then(function () {
        return 2
    })
    .then(() => { Promise.resolve(3) })
    .then(console.log)
// 答案

// undefined

异常穿透(then 中捕获到异常后不会传递给后续 catch)

let promise = new Promise((resolve, reject) => {
	resolve('First resolve');
});

promise.then(value => {
	return value;
})
.then((value) => {
	return new Promise((resolve, reject) => {
  	setTimeout(() => {
    	reject('ERROR');
    }, 2000);
  });
})
.then(value => {
	console.log(value);
}, reason => {
	console.log('Rejected: ' + reason); // Rejected: ERROR
})
.then(value => {
  throw new Error('Throw Error');
})
.then(value => {
  console.log(value);
}, reason => {
	console.log('Then: ' + reason); // Then: Error: Throw Error
})
.catch(err => {
	console.log('Catch: ' + err);
});
Promise.reject(1)
    .then(res => {
        console.log(res);
    })
    .then(res => { console.log(res) },
        rej => {
            console.log(`rej****${rej}`);
        })
    .catch(err => {
        console.log(`err****${err}`);
    })

// 答案:

// rej****1
Promise.reject(1)
    .then(res => {
        console.log(res);
    })
    .then(res => { console.log(res) },
        rej => {
            console.log(`rej****${rej}`);
        })
    .then(res => {
        console.log(`res****${res}`);
    }, rej => {
        console.log(`rej****${rej}`);
    })
    .catch(err => {
        console.log(`err${err}`);
    })

// 答案:

// rej****1
// res****undefined

catch 之后还可以继续 then

let promise = new Promise((resolve, reject) => {
	resolve('First resolve');
});

promise.then(value => {
	return value;
})
.then((value) => {
	return new Promise((resolve, reject) => {
  	setTimeout(() => {
    	reject('ERROR');
    }, 2000);
  });
})
.then(value => {
	console.log(value);
}, reason => {
	console.log('Rejected: ' + reason); // Rejected: ERROR
})
.then(value => {
  throw new Error('Throw Error');
})
.then(value => {
  console.log(value);
})
.catch(err => {
	console.log('Catch: ' + err); // Catch: Error: Throw Error
  return 'Catch Error';
})
.then(value => {
	console.log('Then: ' + value); // Then: Catch Error
});

p2 依赖于 p1 的状态,会导致 p2 的状态无效,会等待 p1 的状态

var p1 = new Promise((resolve, reject) => {
	setTimeout(function(){
  	reject(new Error('fail'));
  }, 3000);
})

var p2 = new Promise((resolve, reject) => {
	setTimeout(function(){
  	resolve(p1);
  }, 1000);
})

p2.then(res => console.log('res', res))
	.catch(err => console.log('p2-err-', err))

reject 不会等待参数类型是 promise 的结果

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
});
const p2 = new Promise((resolve, reject) => {
  reject(p1);
});
p2.then(value => {
  console.log('value', value);
}, err => {
  console.log('err', err);
});

状态固化后面的代码依然会执行,但是 resolve、reject 后面的错误无法被捕获

var p1 = new Promise((resolve, reject) => {
	resolve(1);
  console.log(a) // resolve 后的错误不会被捕获
})

p1.then(res => console.log(res))
	.catch(err => console.log('p1-err-', err))

console.log(3);

但如果 resolve、reject 之后的代码不报错,还是会正常执行:

var p1 = new Promise((resolve, reject) => {
	resolve(1);
  console.log(233)
})

p1.then(res => console.log(res))
	.catch(err => console.log('p1-err-', err))

console.log(3);
var p1 = new Promise((resolve, reject) => {
	reject(new Error());
  console.log(2)
})

p1.then(res => console.log(res))
	.catch(err => console.log('p1-err-', err))

console.log(3);
var p1 = new Promise((resolve, reject) => {
	reject(new Error());
  console.log(a); // reject后的错误不会被捕获
})

p1.then(res => console.log(res))
	.catch(err => console.log('p1-err-', err))

console.log(3);

Promise 方法

Promise.all()

处理多个 promise

const fs = require('fs');
var p1 = new Promise((resolve, reject) => {
	fs.readFile('./name.txt', 'utf-8', function(err, data){
  	if(err){
    	console.log(err);
    }
    resolve(data);
  })
})
var p2 = new Promise((resolve, reject) => {
	fs.readFile('./number.txt', 'utf-8', function(err, data){
  	if(err){
    	console.log(err);
    }
    resolve(data);
  })
})
var p3 = new Promise((resolve, reject) => {
	fs.readFile('./score.txt', 'utf-8', function(err, data){
  	if(err){
    	console.log(err);
    }
    resolve(data);
  })
})
var p = Promise.all([p1, p2, p3]);

成功会返回新的 promise 对象数组

var p1 = new Promise((resolve, reject) => {
	setTimeout(function(){
		resolve('p1: 1000')  
  }, 1000)
})
var p2 = new Promise((resolve, reject) => {
	setTimeout(function(){
		resolve('p2: 2000')  
  }, 2000)
})
var p3 = new Promise((resolve, reject) => {
	setTimeout(function(){
		resolve('p3: 3000')  
  }, 3000)
})
var p = Promise.all([p1, p2, p3]);
p.then(res => console.log('res', res))
	.catch(err => console.log('err', err))

有一个失败就返回第一个失败的返回值

var p1 = new Promise((resolve, reject) => {
	setTimeout(function(){
		reject('p1: 1000')  
  }, 1000)
})
var p2 = new Promise((resolve, reject) => {
	setTimeout(function(){
		reject('p2: 2000')  
  }, 2000)
})
var p3 = new Promise((resolve, reject) => {
	setTimeout(function(){
		reject('p3: 3000')  
  }, 3000)
})
var p = Promise.all([p1, p2, p3]);
p.then(res => console.log('res', res))
	.catch(err => console.log('err', err))
var p1 = new Promise((resolve, reject) => {
	setTimeout(function(){
		resolve('p1: 1000')  
  }, 1000)
})
var p2 = new Promise((resolve, reject) => {
	setTimeout(function(){
		reject('p2: 2000')  
  }, 2000)
})
var p3 = new Promise((resolve, reject) => {
	setTimeout(function(){
		reject('p3: 3000')  
  }, 3000)
})
var p = Promise.all([p1, p2, p3]);
p.then(res => console.log('res', res))
	.catch(err => console.log('err', err))

Promise.race()

任意一个 promise 失败或者成功, 就会返回第一个固化的 promise 的结果

var p1 = new Promise((resolve, reject) => {
	setTimeout(function(){
		resolve('p1: 1000')  
  }, 1000)
})
var p2 = new Promise((resolve, reject) => {
	setTimeout(function(){
		reject('p2: 2000')  
  }, 2000)
})
var p = Promise.race([p1, p2]);
p.then(res => console.log('res', res))
	.catch(err => console.log('err', err))
var p1 = new Promise((resolve, reject) => {
	setTimeout(function(){
		reject('p1: 1000')  
  }, 1000)
})
var p2 = new Promise((resolve, reject) => {
	setTimeout(function(){
		reject('p2: 2000')  
  }, 2000)
})
var p = Promise.race([p1, p2]);
p.then(res => console.log('res', res))
	.catch(err => console.log('err', err))

Promise.resolve()

thenable 能把对象转为 promise

var thenable = {
	then: function (resolve, reject) {
  	resolve(1);
  }
}
var p = Promise.resolve(thenable)
p.then(function(value) {
	console.log(value)
})
setTimeout(function() {
	console.log(2);
})

Promise.resolve().then(function() {
	console.log(1);
})
console.log(3);

Promise.any()

E12 新增的 Promise 的方法

  • 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
  • 如果有一个Promise成功,则返回这个成功结果
  • 如果所有Promise都失败,则报错
// 当有成功的时候,返回最快那个成功
function fn(time, isResolve) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      isResolve ? resolve(`${time}毫秒后我成功啦!!!`) : reject(`${time}毫秒后我失败啦!!!`)
    }, time)
  })
}

Promise.any([fn(2000, true), fn(3000), fn(1000, true)]).then(res => {
  console.log(res) // 1秒后 输出  1000毫秒后我成功啦
}, err => {
  console.log(err)
})

// 当全都失败时
function fn(time, isResolve) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      isResolve ? resolve(`${time}毫秒后我成功啦!!!`) : reject(`${time}毫秒后我失败啦!!!`)
    }, time)
  })
}

Promise.any([fn(2000), fn(3000), fn(1000)]).then(res => {
  console.log(res)
}, err => {
  console.log(err) // 3秒后 报错 all Error
})

手写 Promise

const PENDING = 'PENDING',
      FULFILLED = 'FULFILLED',
      REJECTED = 'REJECTED'; // 三种状态

class MyPromise {
  constructor(executor) { // 实例化时传参:executor 执行器
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;

    this.onFulfilledCallbacks = []; // 订阅池
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      // 处理 resolve 传入的值也是 Promise 的情况
      if (value instanceof MyPromise) {
        value.then(resolve, reject);
        return; // 递归调用,阻止向下继续执行
      }

      if (this.status === PENDING) {
        // 必须得在 status 为 PENDING 状态下时,才能改变状态
        this.status = FULFILLED;
        this.value = value;

        // 发布
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        
        // 发布
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

    try {
      executor(resolve, reject);
    } catch (err) {
      // 如果在 executor 中直接抛出错误,得用 try-catch 捕获下,并走 reject
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    // 防止 .then() 不传参
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
    // then 每次都返回一个新的 Promise
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            // onFulfilled 有可能返回一个普通值,也有可能返回一个 Promise
            // 所以需要一个函数来判断和处理这个返回值 x
            resolvePromise(promise2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      }
      if (this.status === PENDING) {
        // 订阅
        this.onFulfilledCallbacks.push(() => {
          // PENDING状态下回调也要异步
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
        });
      }
    });
    return promise2;
  }
  
  // catch() 相当于.then(null, () => {})
  catch(errorCallback) {
  	return this.then(null, errorCallback);
  }

  // 1. finally 无论外边的Promise成功还是失败,都要走finally的回调,并且回调不带参数
  // 2. finally 不返回 Promise 时, 走 then 或者 catch 取决于外边 Promise
  // 3. 如果 finally 内部有 promise 并且有延迟处理,整个finally会等待
  // 4. finally的promise如果是reject,优先级更高
  // 5. finally的promise如果是resolve,则外边优先级更高
  finally(finallyCallback) {
    // finally是实例方法,得 return this.then
    return this.then(value => {
      // finally 能执行异步,所以 return 一个 MyPromise.resolve
      // finally 本身没有状态
      // 所以执行完cb后,再调用 then 返回 finally 之前的 resolve 情况下的 value
      return MyPromise.resolve(finallyCallback()).then(() => value);
    }, reason => {
      // onRejected也得调用 MyPromise.resolve
      // 因为 finally本身不影响外边的状态
      // 在 finally 中return Promise 且 返回 rejected 的情况下
      // MyPromise.resolve一个rejected的Promise,最终状态也会是这个新的Promise的rejected状态
      return MyPromise.resolve(finallyCallback()).then(() => {
        throw reason;
      });
    });
  }

  // Promise 的 静态方法 resolve、reject
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    }

    return new MyPromise((resolve, reject) => {
      resolve(value);
    });
  }

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

  static all(promiseArr) {
    let resArr = [],
        count = 0;

    return new MyPromise((resolve, reject) => {
      promiseArr.map((promise, index) => {
        if (isPromise(promise)) {
          promise.then(value => {
            formatResArr(value, index, resolve);
          }, reject);
        } else {
            formatResArr(promise, index, resolve);
        }
      });
    });

    // value是返回值,index是数组索引,resolve是回调
    function formatResArr(value, index, resolve) {
      resArr[index] = value; // 保证结果和数组顺序一致

      if(++count === promiseArr.length) { // 保证全部结果都成功返回后再返回最终结果
        resolve(resArr);
      }
    }
  }

  static allSettled(promiseArr) {
    if (!isIterator(promiseArr)) {
      throw new TypeError(promiseArr + ' is not iterable (cannot read property Symbol(Symbol.iterator))');
    }
    
    let resArr = [],
        count = 0;

    return new MyPromise((resolve, reject) => {
      // 数组为空的时候,返回空数组
      if (promiseArr.length === 0) {
        resolve([]);
      } else {
      	promiseArr.map((promise, index) => {
          if (isPromise(promise)) {
            promise.then(value => {
              formatResArr('fulfilled', value, index, resolve);
            }, reason => {
              formatResArr('rejected', reason, index, resolve);
            });
          } else {
            formatResArr('fulfilled', promise, index, resolve);
          }
        });
      }
    });

    function formatResArr(status, value, index, resolve) {
      switch (status) {
        case 'fulfilled':
          resArr[index] = {
            status,
            value
          }
          break;
        case 'rejected':
          resArr[index] = {
            status,
            reason: value
          }
          break;
        default:
          break;
      }
      if (++count === promiseArr.length) {
        resolve(resArr);
      }
    }
  }

  static race(promiseArr) {
    return new MyPromise((resolve, reject) => {
        if (promiseArr.length === 0) {
          resolve();
        } else {
          promiseArr.map(promise => {
            // if (isPromise(promise)) {
            //     promise.then(resolve, reject);
            // } else {
            //     resolve(promise);
            // }
            MyPromise.resolve(promise).then(resolve, reject);
          });
        }
    });
  }
  
  static any(promiseArr) {
    if (!promiseArr.length) throw new Error('No Promise passed');
    return new MyPromise((resolve, reject) => {
      let count = 0, errors = [];
      promiseArr.forEach((promise, idx) => {
        MyPromise.resolve(promise).then(resolve).catch(err => {
          errors[idx] = err;
          if (++count === promiseArr.length) {
            reject(new Error('No Promise in Promise.any was resolved'))
          }
        })
      });
    });
  }
}

// 判断是否为 Promise
function isPromise(x) {
  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    let then = x.then;
    return typeof then === 'function';
  }
  return false;
}

// 判断是否为可迭代对象
function isIterator(value) {
  return value !== undefined && value !== null && typeof value[Symbol.iterator] === 'function';
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
  	reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'));
  }
  
  let called = false; // 防止 resolve、reject 都调用的情况
  
  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    try { // 防止 then 抛错 throw Error
    	let then = x.then;

      if (typeof then === 'function') { // 认定为 Promise
				then.call(x, y => {
          if (called) return;
          called = true;
        	resolvePromise(promise2, y, resolve, reject);
        }, r => {
          if (called) return;
          called = true;
        	reject(r);
        });
      } else {
        resolve(x);
      }
    } catch(err) {
      if (called) return;
      called = true;
    	reject(err);
    }
  } else {
  	resolve(x);
  }
}

MyPromise.defer = MyPromise.deferred = function() {
	let deferred = {};
  
  deferred.promise = new MyPromise((resolve, reject) => {
  	deferred.resolve = resolve;
    deferred.reject = reject;
  });
  
  return deferred;
}

module.exports = MyPromise;

实现 promisify 与 promisifyAll

原生 readFile

const fs = require('fs');

fs.readFile('./index.text', 'utf-8', (err, data) => {
	if (err) {
  	console.log(err);
  } else {
  	console.log(data);
  }
});

bluebird 库

const fs = require('fs');
const bluebird = require('bluebird');

const readFile = bluebird.promisify(fs.readFile);

readFile('./index.test', 'utf-8').then(value => {
	console.log(value);
}, reason => {
	console.log(reason);
});

实现代码

module.exports = {
	function promisify(fn) {
    return function(...args) {
      return new Promise((resolve, reject) => {
        fn.call(null, ...args, (err, data) => {
          if (err) {
            reject(err);
          } else {
            resolve(data);
          }
        });
      });
    }
  },
  function promisifyAll(fns) {
  	Object.keys(fns).map(fnName => {
    	if (typeof fns[fnName] === 'function') {
      	fns[fnName + 'Async'] = this.promisify(fns[fnName]);
      }
    });
    return fns;
  }
}

测试代码

const fs = require('fs');
const util = require('./util');

const readFile = util.promisify(fs.readFile);

readFile('./data/user.json', 'utf-8').then(value => {
  console.log('value', value);
}, reason => {
  console.log('reason', reason);
});

const fsFunctions = util.promisifyAll(fs);

fsFunctions.readFileAsync('./data/class.json', 'utf-8').then(value => {
  console.log('valueAsync', value);
}, reason => {
  console.log('reasonAsync', reason);
});

面试

为什么 Promise 执行是同步,p.then 是异步

  • promise存在的目的不是单纯的为了解决回调地狱
  • 异步的原因是,不希望去阻塞除 promise 相关外的其他代码

为的是解决下面问题:

  1. 把 jquery 的 async 改为 false 会导致下边的同步代码 I am a crazy guy. 得等异步请求数据返回之后才会执行,而这不是我们想要的
  2. 我们不但想让 I am a crazy guy.同步执行,还想把数据请求返回后的数据处理逻辑(jquery的success回调)分离出来,promise的优势就出来了

jquery:

promise包裹:

  • 把数据处理逻辑分离出来了
  • 同步代码不用等待了

Promise 失败重试,可指定重试次数

来源:https://www.mulingyuer.com/archives/758/

//模拟异步请求
function axiosFn() {
    return new Promise((resolve, reject) => {
        const flge = Math.random(); //随机值
        setTimeout(() => {
            //大于0.7就是成功
            if (flge > 0.7) {
                return resolve(flge);
            } else {
                return reject(flge);
            }
        }, 1000)
    })
}

/**
 * @description: promise 失败重试方法
 * @param {*} fn 异步函数
 * @param {*} times 次数
 * @Date: 2022-02-20 22:47:54
 * @Author: mulingyuer
 */
Promise.retry = function(fn, times) {
    return new Promise(async (resolve, reject) => {
        while (times--) {
            try {
                const res = await fn();
                //请求成功,结束while循环
                return resolve(res);;
            } catch (error) {
                //请求失败
                if (times > 0) {
                    console.log(`请求失败正在重试,还剩${times}次,错误信息为:${error}`);
                } else {
                    console.log(`请求失败,重试${times}次后,还是失败,错误信息为:${error}`);
                    return reject(error);

                }
            }
        }
    })
}

//测试
Promise.retry(axiosFn, 3).then(res => {
    console.log(`获得的数据为:${res}`);
}).catch(error => {
    console.log(`失败了,错误信息为:${error}`);
});

宏任务、微任务

  • 微任务
    • promise,process.nextTick() 优先级更高,优先执行
  • 宏任务
    • setTimeout 等
  • 执行宏任务之前,会先把队列中的微任务一个个执行
Promise.resolve().then(() => {
	console.log(1);
  setTimeout(() => {
  	console.log(2);
  }, 0)
})
console.log(3);
setTimeout(() => {
	console.log(4);
  Promise.resolve().then(() => {
  	console.log(5);
  })
}, 0)
最后更新于 2023-07-20