Promise一篇吃透,手把手实现手写Promise
异步处理ES6办法
为了兼容以往对异步处理的方法,以及解决以往异步处理事件和回调的缺陷,ES6使用了Promise A+规范,来专门处理异步问题:避免回调地狱,使异步代码更加整洁、统一。
promise A+规范:
在promiseA+中,把每一个异步任务都看做一个promise对象,每一个任务对象都有两个阶段(unsettled,settled),三个状态(pending,resolved,rejected,)
两个阶段:
- unsettled:事情未解决阶段,事情还没有结果。
- settled:事情已解决阶段,无论是成功,还是失败,已经有结果了。
三个状态:
- padding:挂起,处于未解决阶段。事情还没有结果输出,promise对象为挂起状态。
- resolved:已处理,处于已解决阶段。已经有了成功的结果,可以按照正常逻辑进行下去,并可以传递一个成功信息,后续可以用.then的onFulfiled处理。
- rejected:已拒绝,处于已解决阶段,已经有了失败的结果,无法按照正常逻辑进行下去,可以传递一个错误信息,后续可以用.then的onRejected处理,或使用catch处理。
两个阶段是由unsettle -> settled,不可逆。
三个状态是由padding -> resolved/rejected,不可逆,且resolved和rejected之间的状态不可以互相切换。
也就是说,promise对象一旦有了结果,结果不可改变,不可消失。
一张图看懂promise A+:
ES6为我们提供了PromiseAPI,来实现Promise对象的书写。
promise的基本使用
const p1 = new Promise((resolve, reject) => {
//此部分为同步部分
//此部分决定Promise的状态,未决定之前为Padding不能进行后续对该Promise的处理
//resolve和reject只能接受和传递一个参数
//resolve将Promise对象推入已处理状态
//reject将Promise对象推入已拒绝状态
resolve("请求成功");
//状态一旦改变就不可以更改
//reject('请求失败')
});
//处理Promise的结果,为异步部分。
//若对应的promise为pending,则会加入作业队列,等待Promise结果。
//若Promise结果为resolved或rejected,加入微任务队列等待执行JS引擎轮询到立马执行。
p1.then(
(value) => {
//此部分为onFulfilled,也就是thenabled处理,Promise状态为resolved接收参数并执行此部分代码。
console.log(`Promise已处理,接收到传值:${value}`);
},
(err) => {
//此部分为onRejected,也就是catchabled处理,Promise状态为reject接收参数并执行此部分代码。
//Promise对象的then方法调用中也可以不写此部分,不进行错误处理,在Promise对象的catch方法中进行错误处理。
console.log(`Promise已拒绝,接收到原因:${err}`);
}
);
//Promise并没有消除回调,只是让回调变得可控了。
Promise封装Ajax
function myAjax(obj) {
//返回一个Promise对象
return new Promise((resolve, reject) => {
let xhr = null;
//在不同浏览器下创建ajax对象
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject();
}
//设置ajax默认值
obj.type = obj.type || "get";//默认请求方法为get
obj.data = obj.data || null;
obj.async = obj.async || true;//默认请求为异步
if (obj.type === "post") {
let data = toQueryString(obj.data);
xhr.setHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.open("post", url, obj.async);
xhr.send(data);
} else {
let url = obj.url + toQueryString(obj.data);
xhr.open("get", url, obj.async);
xhr.send();
}
xhr.onreadyStateChange = function () {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 && xhr <= 300) || xhr == 304) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(JSON.parse(xhr.status));
}
}
};
});
}
//请求参数处理
function toQueryString(data) {
if (data) {
let url = "";
for (let [key, value] of Object.entries(data)) {
url = `${key}&${value}`;
}
return url;
} else {
return "";
}
}
Promise的串联:
promise.then(),promise.catch(),promise.resolve()返回的都是一个Promise对象。所以我们可以继续对新返回的Promise对象进行处理。所以Promise可以链式调用。后续的Promise一定会等到前面的Promise状态返回并处理完成后,才会进入已处理阶段。
在当前Promise的阶段在已解决阶段时,Promise的处理返回的Promise对象默认是resolved状态。如果在Promise的处理过程中抛出错误,或是返回一个新的Promise对象状态为reject,将会影响到Promise处理返回的新的Promise状态。
Promise的AIP:
- Promise.then:接收两个参数,注册两个后续处理函数,第一个参数注册resolved时的处理函数,第二个参数注册rejected时的处理函数。我们通常用then来处理resolved,第二个参数可以不填
- Promise.catch:注册rejected时的处理函数,链式调用时,对catch以前的rejected都可以统一处理。
- Promise.finally:注册一个处理函数,无论resolved还是rejected都会执行。
- Promise.resolve:返回一个resolved状态的Promise对象,并可以传一个参数。
- Promise.reject:返回一个rejected状态的Promise对象,并可以传递一个参数。
- Promise.all:接收一个Promise对象的数组,当所有Promise对象都进入已决阶段时,执行并返回一个新的Promise对象 。若所有Promise的处理结果均为resolved,那么新返回的Promise对象状态为resolved,并传递一个拥有所有Promise对象resolved传值,并且按顺序排列的数组。若Promise中有rejected状态,则新的Promise对象状态也为rejected,并传递第一个rejected的结果。
- Promise.race:接收一个Promise对象的数组,以第一个进入已决阶段的Promise结果为准。返回第一个已决Promise的结果作为新的Promise对象的结果。
- Promise.allSettled:接收一个Promise对象的数组,等待所有Promise已决,便进入resolved状态,传递所有Promise结果的数组。
- Promise.any:接收一个Promise对象的数组,当出现第一个resolve状态的Promise时,发生短路,并传递resolve值。若全部rejected则会抛出一个记录错误信息的数组。
手写Promise
//起步构建
// 1.用类创建Promise,类中需要有个执行器executor
// 2.执行者中发生错误,交给异常状态处理
// 3.执行者中状态只能触发一次,状态触发一次之后,不能修改状态
// 4.执行者中的this,由调用执行者的作用域决定,因此我们需要将执行者中的this绑定为我们创建的Promise对象。
// 5.在构造函数中需要为Promise对象创建status和value记录Promise的状态和传值。
class MyPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.status = MyPromise.PENDING;
this.value = null;
this.callbacks = [];
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value
setTimeout(() => {
this.callbacks.map(item => {
item.onFulfilled(this.value);
})
})
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = reason
setTimeout(() => {
this.callbacks.map(item => {
item.onRejected(this.value);
})
})
}
}
//开始写then方法
//1.then接收2个参数,一个成功回调函数,一个失败回调函数
//2.then中发生错误,状态为rejected,交给下一个then处理
//3.then返回的也是一个Promise
//4.then的参数值可以为空,可以进行传值穿透
//5.then中的方法是异步执行的
//6.then需要等promise的状态改变后,才执行,并且异步执行
//7.then是可以链式操作的
//8.then的onFulfilled可以用来返回Promise对象,并且then的状态将以这个Promise为准
//9.then的默认状态是成功的,上一个Promise对象的状态不会影响下一个then的状态
//10.then返回的promise对象不是then相同的promise
then(onFulfilled, onRejected) {
if (typeof onFulfilled != 'function') {
onFulfilled = value => value
}
if (typeof onRejected != 'function') {
onRejected = reason => reason
}
let promise = new MyPromise((resolve, reject) => {
if (this.status == MyPromise.FULFILLED) {
setTimeout(() => {
this.parse(promise, onFulfilled(this.value), resolve, reject)
});
}
if (this.status == MyPromise.REJECTED) {
setTimeout(() => {
this.parse(promise, onRejected(this.value), resolve, reject)
})
}
if (this.status == MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
this.parse(promise, onFulfilled(value), resolve, reject)
},
onRejected: reason => {
this.parse(promise, onRejected(reason), resolve, reject)
}
});
}
})
return promise
}
//整理冗余代码
parse(promise, result, resolve, reject) {
if (promise == result) {
throw new TypeError('Chaining cycle detected for promise')
}
try {
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
//Promise的静态方法,resolve
static resolve(value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(resolve, reject)
} else {
resolve(value)
}
})
}
//Promise的静态方法,reject
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
//Promise的静态方法,all
static all(promises) {
let values = [];
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
if (promise.status == MyPromise.FULFILLED) {
values.push(promise.value)
} else if (promise.status == MyPromise.REJECTED) {
reject(promise.value)
}
if (values.length == promises.length) {
resolve(values)
}
});
})
}
//Promise的静态方法,race
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(value => {
resolve(value)
})
});
})
}
//Promise的静态方法,race
catch (onRejected) {
return this.then(null, onRejected)
}
}
Async Await
Async Await是ES6中对Promise的进一步封装语法糖。它能使得我们的Promise的异步代码,看上去像是同步书写。Promise虽然可以解决多个异步任务的执行顺序问题,但写法依然采用了回调的形式,当多个Promise嵌套使用时,依然会变得很难看。采用Async Await可以很好的解决这个问题。
Async Await的原理借鉴了ES5中的生成器。目的是化简Promise API。
async加在函数声明之前,使得整个函数内部,变为Promise的同步部分。
await必须在async函数中使用,后面一般跟一个Promise对象,得到promise对象的thenabled的状态数据。若await后面非Promise对象,则返回该值。
await下一行开始的代码,需等待await后的表达式执行完毕后,才会执行。
面试真题与解答
- 封装setTimeout,模拟setInterval
//封装setTimeout
function delay(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}
async function mySetTimeout(time) {
await delay(time);
console.log(`等待${time}s,执行...`);
}
mySetTimeout(2000);
//模拟setInterval
async function mySetInterval(time) {
let i = 1;
while (true) {
await delay(time);
console.log(i++);
}
}
function delay(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}
mySetInterval(1000);
-
Promise的缺点:
无法取消。
转载自:https://juejin.cn/post/7235906458898317372