1.抽象表达:
1)Promise 是一门新的技术(基于ES6规范)
2)Promise 是 JS 中进行异步编程的新解决方案
ps:旧方案是单纯使用回调函数
2.具体表达:
1)从语法上来说:Promise 是一个构造函数
2)从功能上来说:Promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值
pending 变为 resolved 或
变为 rejected
ps:只有这两种状态,且一个 Promise 对象只能改变一次,无论变为成功还是失败,都有一个结果数据 => 成功称为 value , 失败称为 reason。
图源尚硅谷
1.基本代码
// 创建 promise 对象(pending 状态), 指定执行器函数
const p = new Promise((resolve,reject) =>{
// 在执行器中启动异步任务
setTimeout(() => {
const time = Date.now()
if(time % 2 === 1){
resolve('成功的值' + time) //若成功,调用resolve(), 指定成功的 value 并且变成 resolved 状态
}else{
reject('失败的值' + time) //若失败,调用reject(), 指定成功的 reason 并且变成 rejected 状态
}
},1000)
})
p.then(value =>{
console.log(value)
},reason =>{
console.log(reason)
})
2.使用 Promise 封装基于定时器的异步
function doDelay(time) {
// 创建 Promise 对象
return new Promise((reslove, reject) => {
// 启动异步任务
console.log('启动异步任务')
setTimeout(() => {
console.log('延迟任务开始执行')
const time = Date.now()
if (time % 2 === 1) {
resolve('成功的数据' + time)
} else {
reject('失败的数据' + time)
}
}, time);
});
}
const Promise = doDelay(2000)
Promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
3.使用 promise 封装 ajax 异步请求
function sendAjax(url) {
return new Promise((reslove, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET', rul)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
reslove(xhr.response)
} else {
reject(xhr.status)
}
}
}
xhr.send
});
}
sendAjax('http://localhost:3000/posts')
.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
1.旧方法:必须在启动异步任务之前指定
2.Promise:启动异步任务 => 返回 Promise 对象 => 给 Promise 对象绑定回调函数(一个或多个)
1.什么是回调地狱 ?
回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件
2.回调地狱的缺点?
不便于阅读
不便于异常处理
3.解决方案?
Promise 链式调用
4.终极解决方案?
async/await
1.Promise 构造函数:Promise(executor){}
1)excutor 函数:执行器(resolve,reject)=> {}
2)resolve 函数:内部定义成功时调用的函数 value => {}
3)reject 函数:内部定义失败时调用的函数 reason => {}
ps:executor 会在 Promise 内部立即同步调用,在执行器中执行异步操作
2.Promise.prototype.then 方法:(onResolved,onRejected) => {}
1)onResolved 函数:成功的回调函数 (value) => {}
2)onRejected 函数:失败的回调函数 (reason) => {}
ps:指定用于得到成功 value 的成功回调和用于失败 reason 的失败回调,返回一个新的 Promise 对象
3.Promise.prototype.catch 方法:(onRejected) => {}
onRejected 函数:失败的回调函数 (reasone)=> {}
ps:返回一个 promise 对象,并且处理拒绝的情况。它的行为与调用 Promise.prototype.then(undefined, onRejected) 相同
4.Promise.resolve 方法 (value) => {}
value:成功的数据或 promise 对象
ps:返回一个成功/失败(被解析过)的 promise 对象
5.Promise.reject 方法 (reason)=> {}
reason:失败的原因
ps:返回一个失败的 promise 对象
6.Promise.all 方法 (promises) => {}
promises:包含n个 promise 的数组
ps:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败即为失败
7.Promise.race 方法 (promises) => {}
promises:包含n个 promise 的数组
ps:返回一个新的 promise ,第一个完成的 promise 的结果状态就是最终的结果状态
1.如何改变 promise 的状态?
1)resolve(value):如果当前是 pending 就会变为 resolved
2)reject(reason):如果当前是 pending 就会变为 rejected
3)抛出异常:如果当前是 pending 就会变为 rejected
2.一个 promise 指定多个成功/失败回调函数都会调用吗?
当 promise 改变为对应状态时都会调用
3.改变 promise 状态和指定回调函数的先后顺序?
1)都有可能,正常情况下时先指定回调再改变状态,但也可以改变状态再指定回调
2)如何先改变状态,再指定回调?
①在执行器中直接调用 resolve()/ reject()
//先改状态, 后指定回调函数
new Promise((resolve, reject) => {
resolve(1) // 先改变的状态(同时指定数据)
}).then(// 后指定回调函数, 异步执行回调函数
value => {console.log('value2', value)},
reason => {console.log('reason2', reason)}
)
②延迟更长时间才调用 then():此时异步执行
// 常规: 先指定回调函数, 后改变的状态
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1) // 后改变的状态(同时指定数据), 异步执行回调函数
}, 1000);
}).then(// 先指定回调函数, 保存当前指定的回调函数
value => {},
reason => {console.log('reason', reason)}
)
3)什么时候才能得到数据?
① 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
② 如果先改变状态,则当指定回调时,回调函数被调用,得到数据
4.promise.then() 返回的新 promise 的结果状态由什么决定?
1)简单表达:由 then()指定的回调函数执行结果决定
2)详细表达:
① 如果抛出异常,新 promise 变为 rejected,reason为抛出的异常
② 如果返回的是非 promise 的任意值,新 promise 变为 resolved ,value 为返回的值
③ 如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果
5.promise 中如何串联多个操作任务?
1)promise 的 then()返回一个新的 promise ,可以变成 then()的链式调用
2)通过 then()的链式调用串联多个同步/异步任务
6.promise 异常穿透?
1)当使用 promise 的 then()链式调用时,可以在最后指定失败的回调
2)前面任何操作出了异常,都会传到最后失败的回调中处理
7. 如何中断 promise 链?【面试考点】
(注意这里是中断而不是终止,因为 Promise 无法终止,这里的意思是:在合适的时候,把 pending 状态的 promise 给 reject 掉。)
1) 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
2) 办法: 在回调函数中返回一个 pendding 状态的 promise 对象
【原因】:因为执行then中的成功还是失败的回调,是由上一个promise的状态决定的,如果中间返回一个初始状态的promise对象,then方法不知道执行哪一个回调,后面的then方法也不会执行,promisethen链会中断。
首先,promise 是一个类,所以用class来声明
new Promise((resolve, reject)=>{})
, 因此要传入一个参数(函数),叫做executorclass Promise{
// 构造器
constructor(executor){
// 成功
let resolve = () => {};
// 失败
let reject = () => {};
// 立即执行
executor(resolve,reject)
}
}
new Promise((resolve, reject)=>{resolve(value)})
resolve为成功,接收value参数,并且改变状态为fulfilled,不可以再次改变;new Promise((resolve, reject)=>{reject(reason)}) reject
为成功,接收reason参数,并且改变状态为rejected,不可以再次改变;因此得到以下代码:
class Promise {
constructor(executor) {
// 初始化state为等待态
this.state = 'pending';
//成功的值
this.value = undefined;
//失败的原因
this.reason = undefined;
let resolve = value => {
// state 改变,resolve 调用就会失败
if (this.state === 'pending') {
// resolve调用后,state转化为成功态
this.state = 'fulfilled';
// 储存成功的值
this.value = value;
}
}
let reject = reason => {
// state 改变,reject 调用就会失败
if (this.state === 'pending') {
// resolve调用后,state转化为成功态
this.state = 'rejected';
// 储存成功的值
this.reason = reason;
}
}
// 若 executor 执行报错,则直接调用 reject
try{
executor(resolve,reject);
}catch(err){
reject(err);
}
}
}
promise 有一个叫做 then 的方法,里面有两个参数:onFulfilled,onRejected,成功返回值,失败返回原因
class Promse {
constructor(executor) {... }
// then方法,有两个参数 onFulfilled 和 onRejected
then(onFulfilled, onRejected) {
// 状态为fulfilled,执行onFulfilled,传入成功的值
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
// 状态为rejected,执行onRejected,传入失败的原因
if (this.state === 'rejected') {
onRejected(this.reason)
}
}
}
功能已经基本实现,但无法对付setTimeout
现在可以基本实现简单的同步代码,但是当resolve在setTimeout内执行,then这时的state还是pending的状态,所以我们需要在then调用的时候,将失败和成功存到各自的数组,一旦reject或者resolve,就调用他们
类似订阅发布,先将then里面的两个函数储存起来,由于一个promise可以有多个then,所以存在同一个数组内
// 解决异步调用
class Promise {
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// 成功存放的数组
this.onResolvedCallbacks = [];
// 失败存放的数组
this.onRejectedCallbacks = [];
let resolve = value =>{
if(this.state === 'pending'){
this.state = 'fulfilled'
this.value = value
// 一旦resolve执行,调用成功数组的函数
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = reson => {
if(this.state === 'padding'){
this.state = 'fulfilled'
this.reason = reson
// 一旦reject执行,调用失败数组的函数
this.onRejectedCallbacks.forEach(fn => fn())
}
}
then(onFulfilled,onRejected){
if(this.state === 'fulfilled'){
onFulfilled(this.value)
};
if(this.state === 'rejected'){
onRejected(this.reason)
}
// 当状态state为pending时
if(this.state === 'pending'){
// onFulfilled 传入到成功数组
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value)
})
// onRejected 传入到失败数组
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
}
}
}
我们常常用到 new Promise().then().then()
,这就是链式调用,解决回调地狱问题
1.为了达成链式,我们默认在第一个then里返回一个promise。官方规定了一个方法,在then里面返回一个新的promise,称为promise2:promise2 = new Promise(resolve,reject) => {}
2.当我们在第一个then中 return 了一个参数(需判断)。这个return出来的新promise就是onFulfilled()或onRejected()的值
则规定onFulfilled()或onRejected()的值,即第一个then返回的值,叫做x,判断x的函数叫做resolvePromise
class Promise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// 成功存放的数组
this.onResolvedCallbacks = [];
// 失败存放的数组
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = reson => {
if (this.state === 'padding') {
this.state = 'fulfilled'
this.reason = reson
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
// 声明返回的promise2
let promise2 = new Promise((reslove, reject) => {
if (this.state === 'fulfilled') {
let x = onFulfilled(this.value)
// resolvePromise函数,处理自己return的promise和默认promise2的关系
resolvePromise(promise2, x, reslove, reject)
};
if (this.state === 'rejected') {
let x = onRejected(this.reason)
resolvePromise(promise2, x, reslove, reject)
}
// 当状态state为pending时
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, reslove, reject)
})
this.onRejectedCallbacks.push(() => {
let x = onRejected(this.reason)
resolvePromise(promise2, x, reslove, reject)
})
}
});
// 返回promise,完成链式
return promise2;
}
}
如果x === promise2,则会造成循环引用,自己等待自己完成,则报“循环引用”错误
let p = new Promise(resolve => {
resolve(0);
});
var p2 = p.then(data => {
// 自己等待自己完成,无限循环
return p2;
})
1.判断x
// 完成resolvePromise函数
function resolvePromise(promise2, x, resolve, reject) {
// 循环引用报错
if (x === promise2) {
return reject(new TypeError('Chaining cycle detected for promise'))
}
// 防止多次调用
let called;
// x不是null,且x是对象或者函数
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
// A+ 规定,声明then = x 的then方法
let then = x.then;
// 如果then是函数,则默认是promise
if (typeof then === 'function') {
// 就让then 执行,第一个参数是this,后面是成功回调 和 失败回调
then.call(x, y => {
// 失败和成功只能调用一个
if (called) return;
called = true;
// resolve的结果依旧是 promise 那就继续解析
resolvePromise(promise2, x, resolve, reject);
}, err => {
// 成功和失败只能调用一个
if (called) return;
called = true;
reject(err); // 失败了就失败了
})
} else {
resolve(x); // 直接成功即可
}
} catch (e) {
// 也属于失败
if (called) return;
// 取then出错了那就不继续执行
reject(e)
}
} else {
resolve(x)
}
}
1.规定onFulfilled,onRejected都是可选参数,如果他们不是函数,必须被忽略
2.规定onFulfilled或onRejected 不能同步被调用,必须异步调用,我们用 setTimeout解决异步问题
// 解决其他问题
class Promise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = reson => {
if (this.state === 'padding') {
this.state = 'fulfilled'
this.reason = reson
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
// onFulfilled 不是函数,就忽略onFulfilled,直接返回 value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// onRejected 如果不是函数,就忽略onRejected,直接扔出错误
onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}
let promise2 = new Promise((resolve,reject) => {
if(this.state === 'function'){
// 异步
setTimeout(() => {
try{
let x = onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
},0)
}
if(this.state === 'rejected'){
// 异步
setTimeout(() => {
// 如果报错
try{
let x = onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}
if(this.state === 'pending'){
this.onRejectedCallbacks.push(() => {
// 异步
setTimeout(() => {
try{
let x = onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
})
this.onRejectedCallbacks.push(() => {
// 异步
setTimeout(() => {
try{
let x = onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
})
}
})
// 返回 promise 完成链式
return promise2
}
}
最后加上catch,resolve,reject,race,all 方法
class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onResolvedCallbacks.forEach(fn=>fn());
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled,onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
};
});
return promise2;
}
catch(fn){
return this.then(null,fn);
}
}
function resolvePromise(promise2, x, resolve, reject){
if(x === promise2){
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called;
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if(called)return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, err => {
if(called)return;
called = true;
reject(err);
})
} else {
resolve(x);
}
} catch (e) {
if(called)return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
//resolve方法
Promise.resolve = function(val){
return new Promise((resolve,reject)=>{
resolve(val)
});
}
//reject方法
Promise.reject = function(val){
return new Promise((resolve,reject)=>{
reject(val)
});
}
//race方法
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i=0;i{
for(let i=0;i{
processData(i,data);
},reject);
};
});
}