是什么
异步编程的一种解决方案
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 相关外的其他代码
为的是解决下面问题:
- 把 jquery 的 async 改为 false 会导致下边的同步代码
I am a crazy guy.
得等异步请求数据返回之后才会执行,而这不是我们想要的 - 我们不但想让
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)
Comments NOTHING