一、Promises/A 规范
- Promise 本质是一个状态机
- 有三种状态:
pending
(进行中)、fulfilled
(成功)、rejected
(失败) - 状态只能由
pending
变为另外两种的其中一种,且状态转变不可逆:状态转变只能是pending —> fulfilled
或者pending —> rejected
promise
的状态转换只发生一次
new Promise
时需要传入一个执行器executor
,立即执行- 支持链式调用,
then
可以被调用多次 then
有两个onFullfiled
和onRejected
,可以缺省,取默认值then
的返回值:如果是promise
,等它执行完,成功走下一个promise
的then
,失败走下一个promise
的catch
二、Promises基本用法
1.1 基本过程
- 初始化
Promise
状态(pending
) - 立即执行
Promise
中传入的fn
函数,将Promise
内部resolve、reject
函数作为参数传递给fn
,按事件机制时机处理 - 执行
then(..)
注册回调处理数组(then
方法可被同一个promise
调用多次) Promise
里的关键是要保证,then
方法传入的参数onFulfilled
和onRejected
,必须在then
方法被调用的那一轮事件循环之后的新执行栈中执行。
const promise = new Promise((resolve, reject) => {
let status = true;
if (status) {
resolve('操作成功!');
} else {
reject('操作失败!');
}
});
promise.then(res => {
console.log('成功结果:' + res);
}, error => {
console.log('失败结果:' + error);
});
new Promise((resolve, reject) => {
resolve();
}).then(res => {
console.log('success');
}).catch(error => {
console.log('error');
}).finally(() =>{
console.log('finally');
})
//success
//finally
let promise = new Promise((resolve, reject)=>{
reject("拒绝了");
resolve("又通过了");
});
promise.then((data)=>{
console.log('success' + data);
}, (error)=>{
console.log(error)
});
执行结果: "拒绝了"
1.2 事件循环中Promise
Promise
代码不同于其他函数,对传入Promise
构造方法中的函数不需要显示的调用执行,其会直接执行,且是作为同步任务被执行的。
setTimeout(() => console.log('timeout'))
new Promise((resolve, reject) => {
console.log('Promise')
});
console.log('main')
/*
* Promise
* main
* timeout
*/
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
}).then(res => {
console.log(res) // success
})
二、手写代码
2.1 手写Promise最简20行版本,实现异步链式调用
function Promise(fn) {
this.cbs = [];
const resolve = (value) => {
setTimeout(() => {
this.data = value;
this.cbs.forEach((cb) => cb(value));
});
}
fn(resolve.bind(this));
}
Promise.prototype.then = function (onResolved) {
return new Promise((resolve) => {
this.cbs.push(() => {
const res = onResolved(this.data);
if (res instanceof Promise) {
res.then(resolve);
} else {
resolve(res);
}
});
});
};
2.2 手写简易版本
function myPromise(executor) {
let self=this;
self.status='pending';
self.value=undefined;
self.reason=undefined;
function resolve(value) {
if(self.status==='pending'){
self.value=value
self.status="resolved"
}
}
function reject(reason) {
if(self.status==='pending'){
self.reason=reason
self.status=status
}
}
try{
executor(resolve,reject)
}
catch (e) {
reject(e)
}
}
2.3 核心案例
new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 500);
})
.then((res) => {
console.log(res);
return new Promise((resolve) => {
setTimeout(() => {
resolve(2);
}, 500);
});
})
.then(console.log);
三、Async/Await
async/await
可以将Promise
代码组织的更像同步代码一样,其书写方式就和之前写同步代码一样,只是需要加上相应关键字。
必须在函数上添加async
关键字,从而可以在函数内使用await
,否则的话会报错。
async function request(id) {
const result1 = await ajax('/api/user/getInfo', { id })
// process result1
const result2 = await ajax('/api/user/getOrder', { id })
// process result2
const result3 = await ajax('/api/user/getMessage', { id })
// process result3
}
request(1)
const asyncFunc = (n) => new Promise(res => setTimeout(() => res(n), 5000))
const call = async (n) => {
const result = await asyncFunc(n)
console.log(result)
}
setTimeout(() => {
console.log('event call!!')
}, 2000)
call(50)
// event call!!
// 50
四、题目
红灯3秒亮一次,绿灯1秒亮一次,黄灯2秒亮一次,循环执行
var light = function (timmer, cb) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
cb();
resolve();
}, timmer);
});
};
var step = function () {
Promise.resolve().then(function () {
return light(3000, red);
}).then(function () {
return light(2000, green);
}).then(function () {
return light(1000, yellow);
}).then(function () {
step();
});
}
step();
// 输出1,2,4,3
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
reject()
console.log(2);
})
promise.then(() => {
console.log(3);
}, () => {
console.log("失败的状态")
})
console.log(4);
// 输出 1,2,4,3
new Promise(resolve => {
console.log(1);
resolve(3);
// 先reslove了,执行then的时候已经是fullfilled,就直接执行了
Promise.resolve().then(()=> console.log(4)) // 这个promise执行的比外层的早
}).then(num => {
console.log(num)
});
console.log(2)
// log: 外部promise
// log: 外部第一个then
// log: 内部promise
// log: 内部第一个then
// log: 外部第二个then
// log: 内部第二个then
new Promise((resolve, reject) => {
console.log("log: 外部promise")
resolve()
})
.then(() => {
console.log("log: 外部第一个then")
new Promise((resolve, reject) => {
console.log("log: 内部promise")
resolve()
})
.then(() => {
console.log("log: 内部第一个then")
})
.then(() => {
console.log("log: 内部第二个then")
})
})
.then(() => {
console.log("log: 外部第二个then")
})
// 今日头条面试题
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('settimeout')
})
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// settimeout
参考文章
- Promise 技术调研 - 回调地狱的产生原因与解决方式(Jiahao.Zhang's Blog)
- ES6 Promise的使用和理解
- 【翻译】Promises/A+规范
- 图解 Promise 实现原理(一)—— 基础实现
待看: