Promise是什么?
抽象表达:
Promise是JS中进行异步编程的新方案(旧方案是回调地狱)。
具体表达:
(1)、从语法上来说,Promise是一个构造函数。
(2)、从功能上来说,Promise是用来封装一个异步操作并可以获取其结果的对象。
为什么要用Promise?
(1)、指定回调函数的方式更加灵活:旧的方案必须在启动异步任务前指定回调函数。
- Promise可以先启动异步任务,返回Promise对象后,再给Promise对象绑定回调函数。
- Promise甚至可以在异步任务结束后指定。(得到一个Promise对象说明异步任务启动了,启动很快,但是完成需要时间)
(2)、支持链式调用,可以解决回调地狱的问题:回调地狱就是回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件,这样的写法不便于阅读,也不便于异常处理。
Promise的状态改变
Promise改变状态只能从pending变为resolved或者从pending变为rejected这两种,并且只能改一次,无论成功还是失败,都会返回一个结果。成功的结果数据称为value,失败的结果数据称为reason。
改变promise的状态和指定回调函数谁先谁后?
都有可能,正常情况下是先指定回调再改变状态,但也可先改变状态再指定回调,只有状态改变,回调函数才会执行。那么什么时候才能的得到数据?
①如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
②如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
先改状态再指定回调函数
const p = new Promise((resolve,reject) => {
setTimeout(() => {
console.log('定时器执行')
resolve(Date.now())
}, 1000);
})
setTimeout(() => {
p.then(value => {
console.log('成功的回调',value)
})
}, 2000);
//打印结果:
//定时器执行
//成功的回调 1591627205718
//改状态是在1秒后,指定回调函数延迟更长的时间
先指定回调函数再改状态
const p = new Promise((resolve,reject) => {
setTimeout(() => {
console.log('定时器执行')
resolve(Date.now())//后改变状态(同时指定数据),然后异步执行回调函数
}, 1000);
})
p.then(value => { //先指定回调函数,但是resolve未执行,状态还没改变,所以回调函数会保存起来
console.log('成功的回调',value)
})
//打印结果:
//定时器执行
//成功的回调 1591627539711
Promise的同步和异步
- promise中的excutor函数:执行器:(resolve,reject) => {},会在Promise内部立即同步回调,执行器中异步任务被放入宏/微队列中等待执行。
- .then()也是同步的,.then()和回调函数不是一回事,
- .then()是去指定回调函数的。同步改的状态,同步指定的回调函数,那么执行回调函数的条件就满足了,按理来说我们应该马上执行回调函数。但是.then()中的回调函数(value=>{},reason=>{}),是异步的,即使条件已经满足也不是立即执行的。
- .then()中的同步操作是直接在.then()里面return里执行(也就是value=>{}这个大括号里面执行),异步操作就return一个新的Promise(promise封装异步操作)。
promise.then()返回的新promise的结果由什么决定?
(1)、简单描述:由then()指定的回调函数决定
(2)、详细描述:
a.如果PromiseA返回的是另一个新的PromiseB,PromiseB的结果就是PromiseA的结果
b.如果PromiseA返回是非Promise的任意值,PromiseA变为resolved,value为返回值。
c.如果抛出异常,PromiseA变为rejected,reason为抛出异常的原因。
注意事项:下一个then的结果,和上一个Promise执行value=>{}成功的回调还是reason()=>失败的回调没有关系,而是由value()=>{}或reason()=>{}执行的结果决定。
new Promise((resolve,reject) => {
reject(1)
}).then(
value => console.log('成功的1',value),
reason => {
console.log('失败的1',reason)
return new Promise((resolve,reject) => {
resolve(2)
})
}
).then(
value => {
console.log('成功的2',value)
return 3
},
reason => console.log('失败的2',reason)
).then(
value=> {
console.log('成功的3',value)
throw Error('Error')
},
reason=> console.log('失败的3',reason)
).then(
value => console.log('成功的4',value),
reason => console.log('失败的4',reason)
)
//结果:
失败的1 1
成功的2 2
成功的3 3
失败的4 Error: Error
以上代码说明:
Promise如何串连多个任务?
Promise异常传透(穿透)?
具体点:比如说你中上一个Promise中执行了reject(1),但是后面的then()中并没有写对失败处理的回调(其实相当于写了reason =>{throw reason}才可以把错误向下抛),就会通过.then()一层一层传透过去,直到最后没找到处理失败的回调就会报错:Uncaught (in promise) 1。但是如果你在链式调用中写了.catch(),那么失败的回调就会在这里被捕获,不再往下传透。
注意:then 和 catch 期望接收函数做参数,如果非函数就会发生 Promise 穿透现象,打印的是上一个 Promise 的返回。
new Promise((resolve,reject) => {
reject(1)
}).then(
value => console.log('成功的1',value),
).catch(
reason => {
console.log('reason',reason)
// throw reason // ------>在这里如果不抛异常或者返回失败的Promise,后面的then()就会进入成功回调
// return Promise.reject(reason)
}
).then(
value => console.log('成功的1',value),
reason => console.log('失败的1',reason)
)
// 结果:reason 1
// 成功的1 undefined
中断Promise链?
上面代码中,在catch()里面如果不抛出异常或者返回失败的Promise,后面的then会进入成功的回调,如果抛了异常或返回了失败的Promise,后面的then就会进入失败的回调。如果不想再进入后面的then呢?该怎么办?
解决办法:返回一个pending状态的Promise。 return new Promise(()=>{})
这样为什么后面的就不执行了呢?因为一旦返回新的Promise,这个新的Promise的结果就决定了后面的then执行成功或者失败的回调。但是这个新的Promise一直处于pending阶段,无法获取结果。实现了中断Promise链。
简述Promise A+规范
术语:
要求:
- promise状态
- then方法
- promise解决过程
promise解决过程是一个抽象的操作,其需输入一个promise和一个值,表示为[[Resolve]] (promise,x) 这句话意思:把promise resolvel同时传入x作为值。
promise.then(function(x) {
console.log('会执行这个函数,同时传入x变量的值',x);
})
手写Promise
(function() {
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function Promise(excutor) {
this.status = PENDING;
this.data = undefined;
this.callbacks = [];
const self = this;
function resolve(value) {
if(self.status !== PENDING) return;
this.status = RESOLVED;
this.data = value;
if(self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(callbackObj => {
callbackObj.onResolved(value)
})
});
}
}
function reject(reason) {
if(self.status !== PENDING) return;
this.status = REJECTED;
this.data = reason;
if(self.callbacks.length > 0) {
self.callbacks.forEach(callbackObj => {
callbackObj.onRejected(reason)
})
}
}
try {
excutor(resolve, reject);
} catch (error) {
reject(error)
}
}
Promise.resolve = function(value) {
return new Promise((resolve, reject) => {
if(value instanceof Promise) {
value.then(resolve, reject);
} else {
resolve(value);
}
})
}
Promise.reject = function(reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
Promise.all = function(promises) {
const values = new Array(promises.length); // 指定数组长度
let count = 0;
return new Promise((resolve, reject) => {
promises.forEach((p,index) => {
p.then(
value => {
count++;
values[index] = value;
if(count === promises.length) {
resolve(values);
}
},
reason => {
reject(reason)
}
)
})
})
}
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
promises.forEach((p, index) => {
p.then(
value => {
resolve(value);
},
reason => {
reject(reason);
}
)
})
})
}
Promise.resolveDelay = function(value, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(value instanceof Promise) {
value.then(resolve, reject);
} else {
resolve(value);
}
}, delay);
})
}
Promise.rejectDelay = function(reason, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(reason)
}, delay);
})
}
/**
* 将执行回调函数抽取成一个函数,需要改变then返回的promise的状态,结果有三种情况
* 1:抛出异常,我们用try...catch捕获,
* 2:如果返回了一个promise(判断结果是不是promise的实例,调用then去执行
* 3:如果返回非promise,值就是result
* 如果是pending状态,说明先指定的回调,将回调函数加入队列稍后执行
* 如果是resolve状态或者rejected,说明状态改变,异步执行队列的函数,
*/
Promise.prototype.then((onResolved, onRejected) => {
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const self = this;
return new Promise((resolve, reject) => {
// 提取handle函数,用于改变新的promise的状态
function handle(callback) {
try {
const result = callback(self.data);
if(result instanceof Promise) {
result.then(resolve, reject)
} else {
resolve(result);
}
} catch (error) {
reject(error)
}
}
if(self.status === PENDING) {
self.callbacks.push({ // 存回调,改状态,
onResolved() {
handle(onResolved);
},
onRejected() {
handle(onRejected);
}
})
} else if(self.status === RESOLVED) {
setTimeout(() => {
// 异步执行回调函数
handle(onResolved)
});
} else {
setTimeout(() => {
handle(onRejected)
});
}
})
})
window.Promise = Promise;
})(window);
最后来几道常见面试题吧
题1:下面三个有哪些不同
const promise = new Promise(function(resolve, reject){
resolve(1);
})
// 1
promise.then(() => {
return Promise.resolve(2);
}).then((n) => {
console.log(n)
});
// 2
promise.then(() => {
return 2
}).then((n) => {
console.log(n)
});
// 3
promise.then(2).then((n) => {
console.log(n)
});
// 结果:2 1 2
分析:1:第一个then里面返回一个新的promise,后面的then在下一个事件循环机制才会去执行
2:return 一个2,它不用等下一个事件循环。
3:then 和 catch 期望接收函数做参数,若非函数会发生 Promise 穿透,打印上一个 Promise 的返回。
题2:
let a;
const b = new Promise((resolve, reject) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
}).then(() => {
console.log('promise3');
}).then(() => {
console.log('promise4');
});
a = new Promise(async (resolve, reject) => {
console.log(a);
await b;
console.log(a);
console.log('after1');
await a
resolve(true);
console.log('after2');
});
console.log('end');
// 结果:promise1, undefined, end, promise2, promise3, promise4, Promise:{<pending>}, after1
分析:前面的比较简单就不解释了,有一点比较难理解的是:从 await a 开始,a 是必须等待 Promise 的状态改变才会继续往下执行,可 a 的状态是一直得不到更改的,所以无法执行下面的逻辑。只要在 await a 上面加一行 resolve() 就能让后面的 after 2 得到输出。
题3:如何取消promise?
function wrap(p) {
let resol = null;
let abort = null;
const p1 = new Promise((resolve, reject) => {
resol = resolve;
abort = reject;
});
p1.abort = abort;
p.then(resol, abort);
return p1;
}
const newPromise = wrap(promise);
newPromise.then(res => console.log)
newPromise.abort()
分析:promise 缺陷:无法得知执行到哪,也无法取消,只能被动的等 resolve 或者 reject 执行或者抛错。
思路:外部包裹一层 Promise,并对外提供 abort 方法,这个 abort 方法可以用来 reject 内部的 Promise 对象。
题4:给你若干promise对象,如何保证顺序输出?
// 题:
const makePromise = (value, time) => {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve(value);
}, time)
})
};
function order(promises) {
}
order([
makePromise('a', 3000),
makePromise('b', 5000),
makePromise('c', 2000),
]);
// 答案
function order(promises) {
let dataArr = []
const promise = Promise.resolve();
for (let i = 0; i < promises.length; i++) {
promise = promise.then((data) => {
if (data) {
dataArr.push(data);
}
return promises[i];
});
}
return promise.then(data => {
console.log(data);
})
}
题5:如何并行发送指定数量的请求?
const delay = function delay(interval) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(interval);
}, interval);
})
}
let tasks = [
() => { return delay(1000)},
() => { return delay(1003)},
() => { return delay(1005)},
() => { return delay(1002)},
]
function createRequest(tasks, limit, callback) {
if(typeof limit === 'function') {
callback = limit;
limit = 2;
}
if(typeof limit !== 'number') limit = 2;
if(typeof callback !== 'function') callback = function() {}
class TaskQueue {
constructor() {
this.index = 0;
this.queue = [];
this.results = [];
}
pushTask(task) {
this.queue.push(task);
this.next();
}
next() {
while(this.index < limit && this.queue.length) {
this.index++;
const task = this.queue.shift();
task().then(result => {
this.results.push(result);
}).finally(() => {
this.index--;
this.next();
})
}
if(this.index === 0) callback(this.results);
}
}
const taskQueue = new TaskQueue;
tasks.map(task => taskQueue.pushTask(task));
}
createRequest(tasks, 2, (results) => {console.log(results)})
题6
const promise1 = new Promise((resolve, reject) => { reject() })
promise1.then(null, function() {return 123})
.then(null, null)
.then(
(value) => { console.log('已经完成', value) },
(reason) => { console.log('已经失败', reason) }
)
// 已经完成 ,123 then中onFulfilled,onRejected不是一个函数,可以被忽略,promise1.then返回一个值123,进入onFulFilled状态。
结语
关于promise的介绍就到这啦,需要小伙伴们耐心看完。如有不对的地方,肯请指出哦?!
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!