
一、异步出现的原因
js是单线程的,一次只能干一件事,所以,所有的事情都像排队一样,等着被执行,但是如果其中一件事情在队列中需要耗费很长的时间,那下一个事件就会一直在等待。比如我们打开一个网页,接口请求时间是5秒,那页面等待5秒之后,再渲染页面,页面就会长时间留白,影响用户体验。于是,js在同步机制的缺陷下设计出了异步模式
举个例子
- 1.不论你是先切菜还是先用电饭煲煮饭,都要等一个事情完毕后才能进行下一项,这就是一个同步的例子
- 2.切菜的时候你也可以用电饭煲煮饭 (不用等你切完菜)这就是一个异步的例子。下面用代码演示一下异步
function a() {
console.log("a");
setTimeout(() => {
console.log("a1");
}, 1000);
}
function b() {
console.log("b");
}
a();
b();
二、异步的解决方案
1.回调函数
回调函数的概念:被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数
function b(value) {
var bb = "everyone";
console.log(value + bb);
}
function a(callback) {
let value = "hello ";
setTimeout(() => {
callback(value);
}, 1000);
}
a(b);
回调函数的缺点:容易写出回调地狱(Callback hell)
- 嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身
- 嵌套函数一多,就很难处理错误(不能用try catch,不能直接return)
let a = setTimeout(function a() {
var name1 = "hello ";
try {
setTimeout(function b() {
var name2 = "everyone " + name1;
setTimeout(function c() {
var name3 = "yeah!";
return name3;
console.log(name2 + name3);
}, 1000);
}, 1000);
} catch (e) {
console.log(e, "不能捕获错误");
}
}, 1000);
console.log(a);
正是由于回调函数有缺陷,所以,es2015诞生了promise
2.Promise
Promise的概念:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值
手写Promise(会围绕下图的方法展开)

Promise的使用

Promise一直是pending的状态,只有当调用resolve或者reject方法之后,状态才会改变,这时候会根据是成功还是失败的状态去执行相应的回调函数
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功");
}, 1000);
}).then(
(value) => {
console.log(value, "这是成功返回的值");
},
(reason) => {
console.log(reason, "这是失败的原因");
}
);
以下是手写Promise类核心逻辑的实现
- Promise就是一个类(或者构造函数,这里只讲类),在执行这个类的时候,需要传递一个执行器(executor)进去,执行器会立即执行
- executor里面有两个参数,一个叫resolve(成功),一个叫reject(失败)
- 由于resolve和reject可执行,所以都是函数
class Promise {
constructor(executor) {
let resolve = () => { };
let reject = () => { };
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
}
- Promise有三种状态,分别为成功Fulfilled,失败Rejected,等待Pending。
- Pending--->Fulfilled
- Pending--->Rejected
- 一但状态确定就不可更改
- resolve和reject就是用来更改状态的
- resolve()--->Fulfilled
- reject()--->Rejected
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class Promise {
constructor(exector) {
const resolve = (value) => {
if (this.status !== PENDING) return;
this.status = FUlFILLED;
this.value = value;
};
const reject = (reason) => {
if (this.status !== PENDING) return;
this.status = REJECTED;
this.reason = reason;
};
try {
exector(resolve, reject);
} catch (e) {
reject(e);
}
}
status = PENDING;
value = undefined;
reason = undefined;
fulfilledCallBack = undefined;
rejectedCallBack = undefined;
}
- then方法内部做的事就是判断状态,如果状态是成功,就调用成功的回调函数;状态失败,就调用失败的回调函数。
class Promise{
constructor(executor){...}
status = PENDING;
value = undefined;
reason = undefined;
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
try {
successCallback(this.value);
} catch (error) {
reject(error);
}
}
if (this.status === REJECTED) {
try {
failCallback(this.reason);
} catch (error) {
this.reject(error);
}
}
}
- 现在基本可以实现简单的同步代码,但是当resolve在setTomeout内执行,then时status还是pending等待状态,所以这时候要把successCallback,failCallback存储起来,一旦resolve或者reject就调用他
- 现在基本可以实现简单的同步代码,但是同一个promsie多次调用then,如果resolve或者reject写在setTomeout内执行,只会执行最后一个then你方法,因为failCallback或者failCallback是以一个变量的方式保存的,应该以数组的方式保存

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(exector) {
const resolve = (value) => {
if (this.status !== PENDING) return;
this.status = FUlFILLED;
this.value = value;
if (this.fulfilledCallBack) {
this.fulfilledCallBack.map((item) => {
item(value);
return;
});
}
};
const reject = (reason) => {
if (this.status !== PENDING) return;
this.status = REJECTED;
this.reason = reason;
if (this.rejectedCallBack) {
this.rejectedCallBack.map((item) => {
item(reason);
return;
});
}
};
try {
exector(resolve, reject);
} catch (e) {
reject(e);
}
}
status = PENDING;
value = undefined;
reason = undefined;
fulfilledCallBack = [];
rejectedCallBack = [];
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback(this.value);
} else if (this.status === REJECTED) {
failCallback(this.reason);
} else {
this.fulfilledCallBack.push(successCallback);
this.rejectedCallBack.push(failCallback);
}
}
}
- then方法的参数是可选的
- then() 相当于----> then(value=>value,(reason)=>{throw reason})

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor){...}
status = PENDING;
value = undefined;
reason = undefined;
fulfilledCallBack = [];
rejectedCallBack = [];
then(successCallback, failCallback) {
const successCallback = successCallback?successCallback:(value)=>value
const failCallback = failCallback?failCallback:(error)=>{throw error}
if (this.status === FULFILLED) {
successCallback(this.value);
} else if (this.status === REJECTED) {
failCallback(this.reason);
} else {
this.fulfilledCallBack.push(successCallback);
this.rejectedCallBack.push(failCallback);
}
}
- then方法的链式调用
- then()返回的也是一个promise对象
- 把上一个then方法的返回的值传递给下一个then。
- then() 返回的结果有两种类型,一种是promise对象,这时候在promise的成功或者失败回调里resolve(value)或者reject(reason)
- 一种是非promise对象的普通值,直接返回resolve(value)
- then方法里面不能返回它自己,报“循环引用”错误

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
constructor(executor){...}
status = PENDING;
value = undefined;
reason = undefined;
fulfilledCallBack = [];
rejectedCallBack = [];
then(successCallback, failCallback) {
successCallback = successCallback ? successCallback : (value) => value;
failCallback = failCallback
? failCallback
: (reason) => {
throw reason;
};
let p = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = successCallback(this.value);
getValueType(p, x, resolve, reject);
} catch (e) {
reject(e);
}
});
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = failCallback(this.reason);
getValueType(p, x, resolve, reject);
} catch (e) {
reject(e);
}
});
} else {
this.fulfilledCallBack.push((value) =>
setTimeout(() =>{
try{
getValueType(p, successCallback(value), resolve, reject)
}catch(e){
reject(e)
}
}
)
);
this.rejectedCallBack.push((reason) =>
setTimeout(() =>
try{
getValueType(p, failCallback(reason), resolve, reject)
}catch(e){
reject(e)
}
)
);
}
});
return p;
}
}
const getValueType = (p, x, resolve, reject) => {
if (p === x) {
return reject(
new TypeError("Chaining cycle detected for promise #<Promise>")
);
}
if (x instanceof MyPromise) {
return x.then(resolve, reject);
} else {
return resolve(x);
}
};
- catch捕获错误
- 返回一个promise错误
- 接受一个回调函数,捕获错误
⚠️注意:这里链式then的失败回调和catch都能捕获错误,但是then的失败回调只能捕获当前promises的错误,不能捕获当前then的成功回调函数里面的错误。但是catch能捕获到所有的错误,所以,链式调用的时候,用catch捕获错误
class MyPromise {
constructor(executor){...}
status = PENDING;
value = undefined;
reason = undefined;
fulfilledCallBack = [];
rejectedCallBack = [];
then(){...}
catch(failCallBack) {
return this.then(undefined, failCallBack);
}
}
const getValueType =()=>{...}
- Promise.all(),Promise.race方法
- Promise.all()允许我们按照异步代码调用的顺序,得到异步代码执行的结果
- Promise.race()异步代码调用哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态
class MyPromise {
constructor(executor){...}
status = PENDING;
value = undefined;
reason = undefined;
fulfilledCallBack = [];
rejectedCallBack = [];
then(){...}
catch() {...}
static all(array) {
let result = [];
let key = 0;
return new MyPromise((resolve, reject) => {
function addData(i, value) {
result[i] = value;
key++;
if (key === array.length) {
resolve(result);
}
}
for (let i = 0; i < array.length; i++) {
if (array[i] instanceof MyPromise) {
array[i].then(
(value) => addData(i, value),
(reason) => reject(reason)
);
} else {
addData(i, array[i]);
}
}
});
}
static race(array) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < array.length; i++) {
if (array[i] instanceof MyPromise) {
array[i].then(resolve, reject);
} else {
resolve(array[i]);
}
}
});
}
}
const getValueType =()=>{...}
- Promise.resolve(),Promise.reject()方法
- 如果传入的是promise对象,原封不动的返回
- 如果传入的是普通值,包装成promise对象返回
class MyPromise {
constructor(executor){...}
status = PENDING;
value = undefined;
reason = undefined;
fulfilledCallBack = [];
rejectedCallBack = [];
then(){...}
catch(){...}
static resolve(value) {
if (value instanceof MyPromise) return value;
return new MyPromise((resolve, reject) => {
resolve(value);
});
}
static reject(value) {
if (value instanceof MyPromise) return value;
return new MyPromise((resolve, reject) => {
reject(value);
});
}
}
const getValueType =()=>{...}
- finally无论失败还是成功都会执行
- finally无论结果是成功或者失败,都会被执行一次
- finally后面可以链式调用then方法,来拿到当前这个promise最终返回的结果
- ⚠️注意:这里finally的回调函数可能返回一个promsie对象,那后面的then()要等finally执行完毕再执行,这里就要借助resolve方法
class MyPromise {
constructor(executor){...}
status = PENDING;
value = undefined;
reason = undefined;
fulfilledCallBack = [];
rejectedCallBack = [];
then(){...}
catch(){...}
static resolve(value){...}
finally(callBack) {
return this.then(
(value) => {
return MyPromise.resolve(callBack()).then(() => value);
},
(reason) => {
return MyPromise.resolve(callBack()).then(() => {
throw reason;
});
}
);
}
}
const getValueType =()=>{...}
至此,promise的基本功能已大致实现。其实promise.then也是类似回调函数的思想,只是,区分了成功和失败的回调函数,而且then方法的链式调用,把回调函数的嵌套地狱解决了,扁平化了。但是还是没有达到我们传统的同步代码的可读性,于是Generator出现了。
3.Generator
- generator函数2个特点,一个*(一般写在function后,function*),一个yield。
- generator函数返回的是一个生成器对象,直到我们通过.next调用,这个函数的函数体才会执行。
- 可以利用yield暂停生成器函数这一特点。使用生成器函数,去完成更优的异步体验。
function* fun1() {
console.log("start");
try{
let aa = yield "foo";
}catch(error){
console.log(error,"error")
}
}
const generator = fun1();
console.log(generator,"generator")
const result = generator.next();
console.log(result,"result")
generator.next("bar")
generator.throw("报错啦")
下面看一个例子
function ajax(){
return new Promise(...)
}
function* main(){
const data1 = yeild ajax("http://www.baidu1.com")
console.log(data1)
const data2 = yeild ajax("http://www.baidu2.com")
console.log(data2)
}
const g = main()
const result = g.next()
result.value.then((value)=>{
const result1 = g.next(value)
if(result1.done) return
result1.value.then((value)=>{
let result2 = g.next(value)
if(result3.done) return
})
})
故在Generator函数体中可以拿到result, 这样就可以用同步的写法解决异步问题
function co(generator){
const g = generator()
function handleResult(result){
if(result.done) return
result.value.then((data)=>{
handleResult(g.next(data))
},(error)=>{
g.throw(error)
})
}
handleResult(g.next())
}
async await
- async await是generator的语法糖,相比于generator,async await不需要再去配合一个类似于co这样的执行器,内部的执行过程和generator是完全一样的
- async函数返回一个promise对象
- await只能在async函数里面使用
function* fun() {
let a = yield setTimeout(() => console.log("111", 0));
let b = yield setTimeout(() => console.log("222", 0));
}
async function fun() {
let a = await setTimeout(() => console.log("111", 0));
let b = await setTimeout(() => console.log("222", 0));
}
所以现在我们大部分情况下会使用async await,它是以几乎同步的方式来实现异步