首先先理解如下几个概念的概念:git
同步:一次只能执行一次任务,这个任务执行完以后才能执行下一个,它会阻塞其余任务。es6
异步:能够一块儿执行多个任务。github
回调地狱:回调函数的层层嵌套,致使代码层次过多,很差理解和维护。面试
先简单了解如下Promise的含义和特性:编程
Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更加合理和强大;segmentfault
所谓的Promise,简单来讲就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果;数组
从语法上来讲,Promise是一个对象,从它能够获取异步操做的消息;promise
Promise对象表明的是一个异步操做,有三种状态: pending(等待状态)、 fulfilled(成功状态)、 rejected(失败状态)markdown
1. 对象的状态不收外界的影响;并发
只有异步操做的结果才能决定当前是哪种状态,任何其余操做都没法改变这个状态。
2. 一旦状态改变就不会再变;
Promise对象状态改变只有两种可能:
只要这两种状态发现了,状态就凝固了,不会再变了,会一直保持这个结果,这时称为resolved(已定型)
3. 每一个Promise实例都有一个then方法,一个Promise能够.then多个;
4. 每次执行Promise的时候,都会返还一个新的Promise实例;
有了Promise对象,就能够将异步操做以同步操做的流程表达出来,解决了异步层层嵌套的函数(简称回调地狱)问题。
说到 Promise,咱们首先想到的最核心的功能就是异步链式调用。
没法取消Promise。一旦新建它就会当即执行,没法中途取消;
若是不设置回调函数,Promise内部抛出错误,不会反应到外部;
当处于Pending(进行中)状态时,没法得知目前进展到哪个节点(刚开始仍是即将完成);
注意,为了行文方便,本章后面的resolved统一只指fulfilled状态,不包含rejected状态。
ES6规定,Promise对象时一个构造函数,使用new来生成Promise实例。
let promise = new Promise(() => {//executor执行器,特色是当即执行
})
复制代码
这个Promise实例是一个类,容许接受一个函数做为参数,这个函数叫作executor执行器,特色是当即执行。以下会当即打印:
let promise = new Promise(() => {//executor执行器,做用当即执行
console.log("当即执行");//Promise有三种状态,此处默认Pending状态
})
console.log("222");
/*控制台打印*/
//当即执行
//222
复制代码
函数的能够接受两个参数resolve(表明成功状态)和rejected(表明失败状态),并且每一个Promise实例都有一个
then方法。(注意:resolve和rejected必须配合then一块儿使用,否则会打印空值)
then分别放置了两个函数:
let promise = new Promise((resolve,rejected) => {
resolve('成功');//若是这里调用的是resolve,那边then就会走成功的逻辑;
}).then(data => {//成功
console.log(data);
},err => {//失败
console.log(err);
})
/*控制台打印*/
//成功
let promise = new Promise((resolve,rejected) => {
rejected('失败');//若是这里调用的是rejected,那边then就会走失败的逻辑;
}).then(data => {//成功
console.log(data);
},err => {//失败
console.log(err);
})
// 失败
//上边的代码也能够这样写:
let promise = new Promise((resolve, rejected) => {
resolve('成功');
})
promise.then(data => {
console.log(data);
}, err => {
console.log(err);
})
复制代码
由于Promise实例的状态一旦状态改变就不会再变,因此调用resolve以后再调用rejected,后一步是不会生效的。反之亦然。
let promise = new Promise((resolve,rejected) => {
resolve('成功');
rejected('失败');//这一步不会执行
}).then(data => {//成功
console.log(data);
},err => {//失败
console.log(err);
})
//成功
复制代码
若是Promise实例内部报错,就会变成失败状态,不会执行后边的方法:
let promise = new Promise((resolve, rejected) => {//executor执行器,做用当即执行
throw new Error('内部抛出错误');//内部抛出错误,就会变成失败状态,then就会走失败的逻辑
resolve('成功');//这一步依然不会执行
}).then(data => {//成功
console.log(data);
}, err => {//失败
console.log(err);
})
//Error: 内部抛出错误
// ...
复制代码
新建一个promise.js,并新建一个Promise类,并导出:
//promise.js:
class Promise {
};
module.exports = Promise;//导出
复制代码
将前面的代码引入promise.js:
//callBack.js:
let Promise = require('./promise');
//引入自定义的Promise,至关于下面用的Promise就是自定义的Promise
let promise = new Promise((resolve, rejected) => {
resolve('成功');
}).then(data => {
console.log(data);
}, err => {
console.log(err);
})
复制代码
根据callBack.js的Promise实例,自定义一个基础版的Promise,不考虑其余异常状况:
(1)建立公共的then方法
Promise实例有三种状态,不论是哪种状态都会执行的then方法,因此then方法属于公共的属性。
//promise.js:
class Promise {
then(){
}
};
module.exports = Promise;//导出
复制代码
每个Promise实例都有本身的三个状态,因此将三种状态放到对应的构造函数(constructor)中。
在构造函数中能够拿到Promise实例的状态,由于这个三个状态会常常用到,因此咱们能够把它们存成一个常量。
//promise.js:
const PENDING = "PENDING";//等待状态
const RESOLVED = "RESOLVED";//成功状态
const REJECTED = "REJECTED";//失败状态
class Promise {
constructor(){//构造函数
this.status = PENDING;//Promise实例的默认pending状态
}
then(){
}
};
module.exports = Promise;//导出
复制代码
咱们在callBacks.js的new Promise实例时,传了一个函数(也就是一个执行器),而且这个函数是当即执行的,因此咱们要给构造函数也传递一个executor执行器:
//promise.js:
const PENDING = "PENDING";//等待状态
const RESOLVED = "RESOLVED";//成功状态
const REJECTED = "REJECTED";//失败状态
class Promise {
constructor(executor){//构造函数
this.status = PENDING;//Promise实例的默认pending状态
executor();//默认执行器会当即执行
}
then(){
}
};
module.exports = Promise;//导出
复制代码
callBacks.js的new Promise实例时,executor执行的时候传递了两个函数,而且属于当前的Promise实例,不须要再then中拿到,因此咱们能够在constructor中声明两个函数:成功函数和失败函数。而且将这两个函数传递给executor执行器:
//promise.js:
const PENDING = "PENDING";//等待状态
const RESOLVED = "RESOLVED";//成功状态
const REJECTED = "REJECTED";//失败状态
class Promise {
constructor(executor){//构造函数
this.status = PENDING;//Promise实例的默认pending状态
//成功函数
let resolve = () => {
}
//失败函数
let reject = () => {
}
executor(resolve,reject);//默认执行器会当即执行
}
then(){
}
};
module.exports = Promise;//导出
复制代码
成功函数和失败的函数都接受一个值,value和reason,由于在then也须要用到这两个值,因此咱们须要定义一个变量。咱们在执行完成功和失败函数以后,须要更新Promise的状态:
//promise.js:
const PENDING = "PENDING";//等待状态
const RESOLVED = "RESOLVED";//成功状态
const REJECTED = "REJECTED";//失败状态
class Promise {
constructor(executor){//构造函数
this.status = PENDING;//Promise实例的默认pending状态
this.value = undefined;//成功的值
this.reason = undefined;//失败的缘由
//成功函数
let resolve = (value) => {
this.value = value;
this.status = RESOLVED;//Promise实例状态更新为成功状态:resolved状态
}
//失败函数
let reject = (reason) => {
this.reason = reason;
this.status = REJECTED;//Promise实例状态更新为失败状态:rejected状态
}
executor(resolve,reject);//默认执行器会当即执行
}
then(){
}
};
module.exports = Promise;//导出
复制代码
由于Promise实例的状态一旦改变了就不能再变了,也就是说咱们调用了resolve函数就不能再调用reject函数了,因此咱们必须添加添加状态判断,只要状态时等待状态pending时,才执行函数:
//promise.js:
const PENDING = "PENDING";//等待状态
const RESOLVED = "RESOLVED";//成功状态
const REJECTED = "REJECTED";//失败状态
class Promise {
constructor(executor){//构造函数
this.status = PENDING;//Promise实例的默认pending状态
this.value = undefined;//成功的值
this.reason = undefined;//失败的缘由
//成功函数
let resolve = (value) => {
if(this.status === PENDING){//// 屏蔽调用,调了成功函数以后不能调失败函数
this.value = value;
this.status = RESOLVED;//调用成功以后,Promise状态变成resolved状态
}
}
//失败函数
let reject = (reason) => {
if(this.status === PENDING){
this.reason = reason;
this.status = REJECTED;////调用失败以后,Promise状态变成rejected状态
}
}
executor(resolve,reject);//默认执行器会当即执行
}
then(){
}
};
module.exports = Promise;//导出
复制代码
由于执行器执行的时候内部可能会报错,会抛出异常,这个时候添加try...catch进行逻辑处理,若是内部抛出异常的话至关于调用了reject失败函数:
接下来开始调用then( )方法,then能够放置了两个函数:
那么何时调用哪个方法呢?这要根据Promise实例的状态来判断了:
Promise实例还有订阅发布功能:
未完待续,后续更新~~
//promise.js:
const PENDING = "PENDING";//等待状态
const RESOLVED = "RESOLVED";//成功状态
const REJECTED = "REJECTED";//失败状态
class Promise {
constructor(executor){//构造函数
this.status = PENDING;//Promise实例的默认pending状态
this.value = undefined;//成功的值
this.reason = undefined;//失败的缘由
this.onResolveCallBacks = [];//成功的回调的数组
this.onRejectCallBacks = [];//失败的回调的数组
//成功函数
let resolve = (value) => {
if(this.status === PENDING){//// 屏蔽调用,调了成功函数以后不能调失败函数
this.value = value;
this.status = RESOLVED;//调用成功以后,Promise状态变成resolved状态
this.onResolveCallBacks.forEach(fn => fn())//发布
}
}
//失败函数
let reject = (reason) => {
if(this.status === PENDING){
this.reason = reason;
this.status = REJECTED;////调用失败以后,Promise状态变成rejected状态
this.onRejectCallBacks.forEach(fn => fn())//发布
}
}
//执行器执行时,可能内部报错:
try{
executor(resolve,reject);//默认执行器会当即执行,接受resolve,reject做为参数
}catch(error){
reject(error);//若是执行器执行时发生错误,等价于调用了失败方法
}
}
then(onfulfilled,onrejected){/// then 目前有两个参数:onfulfilled,onrejected
//同步状况:成功以后执行逻辑
if(this.status === RESOLVED){
onfulfilled(this.value);
}
//同步状况:失败以后执行逻辑
if(this.status === PENDING){
onrejected(this.reason);
}
// 若是是异步就先订阅号
if (this.status === PEDING) {
this.onResolveCallBacks.push(() => {
// todo
onfulfilled(this.value)
})
this.onRejectCallBacks.push(() => {
// todo
onrejected(this.value)
})
}
}
};
module.exports = Promise;//导出
复制代码
console.log('A');
function sleep(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
})
};
sleep(1000).then(() => {
console.log('B');
});
//先输出A,延迟1秒以后输出B
//或
console.log('A');
const sleep = ((time)=>{
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
},time)
})
})
sleep(1000).then(()=>{
console.log('B');
})
复制代码
const sleep = ((time)=>{
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
},time)
})
})
async function sleepAsync(){
await sleep(1000);
console.log('B');
}
复制代码
console.log("A");
const sleep = ((time)=>{
return new Promise((resolive)=>{
setTimeout(()=>{
resolve();
},time)
})
})
function* sleepGenerator(time){
yeild sleep(time);
}
sleepGenerator(1000).next().value.then(()=>{
console.log("B");
})
复制代码
类似的面试题:
常常有业务需求,要等几秒才进行下一步操做,也是使用以上的思路
三个亮灯函数已经存在:
思路:
红灯三秒亮一次,绿灯一秒亮一次,黄灯2秒亮一次,意思就是3秒,执行一次 red 函数,2秒执行一次 green 函数,1秒执行一次 yellow 函数,不断交替重复亮灯,意思就是按照这个顺序一直执行这3个函数,这步能够就利用递归来实现。
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
const sleep = ((time,fn)=>{
return new Promise((resolve)=>{
setTimeout(()=>{
fn();//哪一个灯亮
resolve();
},time)
})
})
let step = (()=>{
Promise.resolve().then(()=>{
return sleep(3000,red);
}).then(()=>{
return sleep(2000,green);
}).then(()=>{
return sleep(1000,yellow);
}).then(()=>{
step();
})
})
复制代码
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
//输入1
复制代码
Promise.resolve 方法的参数若是是一个原始值,或者是一个不具备 then 方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为resolved,Promise.resolve 方法的参数,会同时传给回调函数。
then 方法接受的参数是函数,而若是传递的并不是是一个函数,它实际上会将其解释为 then(null),这就会致使前一个 Promise 的结果会穿透下面。
...
学习写做中,持续补充更新,记录很差的地方望指出修改,共同进步~
参考资料: