promise、async/await的提出,已由来已久,很多人都知道这两个都是为了解决回调地狱问题的,面试中很多面试官都喜欢那这两个作比较,那我今天就来说说promise、async/await的区别以及简单的原理实现。避免面试的尴尬!
篇幅比较长,理解和实现两部分(主要在实现),最近看到很多面试的同学都有提到手写promise、async/await的问题,就随手整理了一下,之前也整理了一下唠一唠call、apply和bind以及手动实现(拒绝晦涩难懂))
promise
Promise,简单来说就是一个容器,里面保存着某个未来才会结束的时间(通常是一个异步操作的结果),通过链式调用同步实现异步;
特点:
- 对象的状态不受外界影响;
- 链式调用(依旧没逃出回调的问题)
- 异常处理(catch)
- 状态一旦改变,就不会再变,状态凝固;
具体的可以关注下关于Promise以及你可能不知道的6件事以及聊一聊Promise的坑
Promise/A+实现
这篇剖析Promise内部结构写的很不错,这里也分享给大家,面试当中写个很简单的就可以了,对于源码我们理解即可;
实现Promise的要求:
- 构造一个Promise实例需要给Promise构造函数传入一个函数,传入的函数需要有两个形参,即resolve和reject,注意两个形参都是function类型的参数;
- Promise上还有then方法(多个then可形成链式调用),then方法就是用来指定Promsie对象的状态改变确定执行的操作,resolve时执行第一个函数(onFulfilled), reject时执行第二个函数(onRejected);
- 当状态变为resolve后就不能在改变成reject, 反之同理;
基于以上要求我们可以实现一个简单的Promise:
// promise 三个状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class myPromise {
constructor(executor) {
this.status = PENDING; // 声明初始状态;
this.value = undefined; // fulfilled状态时 返回的信息
this.reason = undefined; // rejected状态时 拒绝的原因;
this.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
this.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
//=>执行Excutor
let resolve = result => { // resolve方法
if (this.status !== PENDING) return;
// 为什么resolve 加setTimeout?
// 2.2.4规范 onFulfilled 和 onRejected 只允许在 execution context 栈仅包含平台代码时运行.
// 这里的平台代码指的是引擎、环境以及promise的实施代码。实践中要确保onFulfilled 和 onRejected方法异步执行,且应该在then方法被调用的那一轮事件循环之后的新执行栈中执行。
setTimeout(() => {
//只能由pending状态=>fulfilled状态(避免调用多次resolve reject)
this.status = FULFILLED;
this.value = result;
this.onFulfilledCallbacks.forEach(cb => cb(this.value));
}, 0);
};
let reject = reason => { // reject方法
if (this.status !== PENDING) return;
setTimeout(() => {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(cb => cb(this.reason))
})
};
// 捕获在executor执行器中抛出的异常
try {
executor(resolveFn, rejectFn);
}
catch (err) {
//=>有异常信息按照rejected状态处理
reject(err);
}
}
// 添加.then方法
then(onFullfilled, onRejected) {
this.onFulfilledCallbacks.push(onFullfilled);
this.onRejectedCallbacks.push(onRejected);
}
// 添加.catch方法
catch(onRejected) {
return this.then(null, onRejected);
}
}
module.exports = myPromise;
不过在面试中有些会要求手写一个符合Promises/A+规范的完美Promise。
首先来看下Promises规范:
Promise 规范有很多,如Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+。ES6 中采用了Promise/A+ 规范。
- 英文版规范: Promises/A+规范
- 中文版规范: Promises/A+规范(中文)
// promise 三个状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class myPromise {
constructor(executor) {
this.status = PENDING; // 声明初始状态;
this.value = undefined; // fulfilled状态时 返回的信息
this.reason = undefined; // rejected状态时 拒绝的原因;
this.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
this.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
//=>执行Excutor
let resolve = result => { // resolve方法
if (this.status !== PENDING) return;
// 为什么resolve 加setTimeout?
// 2.2.4规范 onFulfilled 和 onRejected 只允许在 execution context 栈仅包含平台代码时运行.
// 这里的平台代码指的是引擎、环境以及promise的实施代码。实践中要确保onFulfilled 和 onRejected方法异步执行,且应该在then方法被调用的那一轮事件循环之后的新执行栈中执行。
setTimeout(() => {
//只能由pending状态=>fulfilled状态(避免调用多次resolve reject)
this.status = FULFILLED;
this.value = result;
this.onFulfilledCallbacks.forEach(cb => cb(this.value));
}, 0);
};
let reject = reason => { // reject方法
if (this.status !== PENDING) return;
setTimeout(() => {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(cb => cb(this.reason))
})
};
// 捕获在executor执行器中抛出的异常
try {
executor(resolveFn, rejectFn);
}
catch (err) {
//=>有异常信息按照rejected状态处理
reject(err);
}
}
// 添加.then方法
then(onFullfilled, onRejected) {
onFullfilled = typeof onFullfilled === "function" ? onFullfilled : value => value;
onRejected = typeof onRejected === "function" ? onRejected : reason => {
throw reason;
};
switch (self.status) {
case PENDING:
// 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
return promise2 = new myPromise((resolve, reject) => {
this.onFulfilledCallbacks.push(()=>{
try {
let x = onFullfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
}
catch (e) {
reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
}
});
this.onRejectedCallbacks.push(() => {
try {
let x = onRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
}
catch (e) {
reject(e); // error catch
}
});
});
break;
case FULFILLED:
return promise2 = new myPromise(function (resolve, reject) {
try {
let x = onFullfilled(this.value);
//将上次一then里面的方法传递进下一个Promise状态
this.resolvePromise(promise2, x, resolve, reject);
}
catch (e) {
reject(e);//error catch
}
});
break;
case REJECTED:
return promise2 = new myPromise(function (resolve, reject) {
try {
let x = onRejected(this.reason);
//将then里面的方法传递到下一个Promise的状态里
this.resolvePromise(promise2, x, resolve, reject);
}
catch (e) {
reject(e);
}
});
break;
default:
}
}
// 添加.catch方法
catch(onRejected) {
return this.then(null, onRejected);
}
static deferred() { // 延迟对象
let defer = {};
defer.promise = new myPromise((resolve, reject) => {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
}
static all(promises = []) {
let index = 0,
result = [];
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(val => {
index++;
result[i] = val;
if (index === promises.length) {
resolve(result)
}
}, reject);
}
})
}
static resolvePromise(promise, x, resolve, reject) {
if (promise === x) { // 如果从onFulfilled中返回的x 就是promise2 就会导致循环引用报错
throw new TypeError("type error")
}
let isUsed;
if (x !== null && (x instanceof Object || x instanceof Function)) {
try {
let then = x.then;
if (typeof then === "function") {
//是一个promise的情况
then.call(x,(y) => {
if (isUsed) return;
isUsed = true;
this.resolvePromise(promise, y, resolve, reject);
},(e) => {
if (isUsed) return;
isUsed = true;
reject(e);
})
}
else {
//仅仅是一个函数或者是对象
resolve(x)
}
}
catch (e) {
if (isUsed) return;
isUsed = true;
reject(e);
}
}
else {
//返回的基本类型,直接resolve
resolve(x)
}
}
}
module.exports = myPromise;
Promise测试
npm i -g promises-aplus-tests
promises-aplus-tests Promise.js
promise的原理Promise/A+的实现就结束了。
async/await
- async/await更加语义化,async 是“异步”的简写,async function 用于申明一个 function 是异步的; await,可以认为是async wait的简写, 用于等待一个异步方法执行完成;
- async/await是一个用同步思维解决异步问题的方案(等结果出来之后,代码才会继续往下执行)
- 可以通过多层 async function的同步写法代替传统的callback嵌套,规避掉了Promise的链式调用,代码看起来简单明了;
栗子:
function sleep(wait) {
return new Promise((res,rej) => {
setTimeout(() => {
res(wait);
},wait);
});
}
async function demo() {
let result01 = await sleep(100);
//上一个await执行之后才会执行下一句
let result02 = await sleep(result01 + 100);
let result03 = await sleep(result02 + 100);
// console.log(result03);
return result03;
}
demo().then(result => {
console.log(result);
});
异步编程的最高境界,根本就不用关心它异步,很多人认为它是异步操作的终极解决方案;但是和Promise不存在谁取代谁,因为async/await是寄生于Promise。Generator的语法糖。
对Generator还比较陌生的同学可以看下廖雪峰写的generator,这里就不做过多介绍了。
实现一个简单的async/await
async/await语法糖就是使用Generator函数+自动执行器来运作的。 我们可以参考以下例子
// 定义了一个promise,用来模拟异步请求,作用是传入参数++
function getNum(num){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num+1)
}, 1000)
})
}
//自动执行器,如果一个Generator函数没有执行完,则递归调用
function asyncFun(func){
var gen = func();
function next(data){
var result = gen.next(data);
if (result.done) return result.value;
result.value.then(function(data){
next(data);
});
}
next();
}
// 所需要执行的Generator函数,内部的数据在执行完成一步的promise之后,再调用下一步
var func = function* (){
var f1 = yield getNum(1);
var f2 = yield getNum(f1);
console.log(f2) ;
};
asyncFun(func);
在执行的过程中,判断一个函数的promise是否完成,如果已经完成,将结果传入下一个函数,继续重复此步骤。
结束
如果发现过程遇到什么问题,欢迎及时提出
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。