异步的概念
-
同步、异步指代的是被调用方状态,阻塞、非阻塞指的是调用方的状态
-
Javascript语言的执行环境是"单线程",所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
-
这种模式的好处是实现起来比较简单;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。
-
为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
-
js解决异步的问题本质上都是回调,在适当的时机触发回调函数。
-
什么时候使用异步(耗时操作,并不影响同步代码逻辑)
- 事件监听函数的回调
- 定时器函数的回调
- 网络请求
- io操作
异步代码
- 回调函数:比如在node中,读一个文件
fs.readFile('文件路径.js', (err, res)=>{
// 当读取完文件后,会触发这个回调函数,将读取结果作为参数传递过来
})
回调函数的优点是简单、容易理解,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,多层的嵌套会造成“回调地狱”。
- 事件监听
- 采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
dom.onclick = () => {
// 点击后,将事件回调放到宏任务队列
})
- 发布/订阅
- 我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern)。
- 流程应该是先订阅,再发布
// jQuery
jQuery.subscribe("done", f2);
function f1(){
setTimeout(function () {
// f1的任务代码
jQuery.publish("done");
}, 1000);
}
这种方法的性质与"事件监听"类似,但是明显优于后者。因为我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
promise
promise将回调代码打平,解决了回调地狱的问题,es6按照promiseA+规范实现了官方的promise。
const p1 = new Promise((resolve, reject)=>{
resolve(1);
})
promise的状态
- 本质上,
Promise
也是一个回调。new
一个Promise
实例,将回调函数executor
传入Promise
,在Promise
中,会将resolve
、reject
函数作为executor
函数的参数传递过来,当调用resolve
或者reject
函数,会触发Promse
的状态PromiseState
和值PromiseResult
的改变。 - 在调用
resolve
或者reject
后Promsie
状态会变成已决状态
,在已决状态
时,再次调用resolve
、reject
是没有效果的,也就是决定状态的函数只能调用一次。 - 代码模拟实现
// resolve函数执行后,promise的状态(已决状态)
const FULFILLED = 'fulfilled';
// reject函数执行后,promise的状态(已决状态)
const REJECTED = 'rejected';
// 未调用resolve、reject函数的状态(未决状态)
const PENDING = 'pending';
class Promise{
constructor(executor) {
// promise的状态
this.PromiseState = PENDING;
// promise的值
this.PromiseResult = undefined;
const run = (state, result) => {
// 如果不是pending状态,不能修改状态
if (this.PromiseState !== PENDING) return;
this.PromiseState = state;
this.PromiseResult = result;
};
// resolve函数,调用后会将promise状态改为fulfilled
const resolve = (result) => {
if (this.PromiseState !== PENDING) return;
run(FULFILLED, result);
};
// reject函数,调用后会将promise状态改为rejected
const reject = (err) => {
if (this.PromiseState !== PENDING) return;
run(REJECTED, err);
};
// 直接执行executor函数,将resolve,reject函数作为参数传入,所以executor函数里面的代码是同步的
try {
executor(resolve, reject);
} catch (error) {
// 如果执行executor过程中发生错误,直接调用reject
reject(error);
}
}
}
Promise then链的运行过程
- 上面是Promise改变状态和值的核心代码,而Promise的核心是then链
- 实例的then函数会有两个回调函数
onFulfilled
、onRejected
,当此时的状态为fulfilled
的时候,会将onFulfilled
函数放入异步任务队列,当此时状态为rejected
的时候,会将onRejected
函数放入异步任务队列,当此时的状态为pending
的时候,会将对应的函数放到对应的待执行队列onFulfilledCallbacks
中,等到状态变为已决的时候,再去执行对应待执行队列所有的回调。 - 在执行
onFulfilled
、onRejected
的时候,会将此时的PromiseResult
作为参数传入,所以在then的回调函数中,可以拿到之前resolve
的结果。 - then函数会返回一个新的Promise实例,继续进行Promise参数,就形成了then链
- 注册then的时候,Promise状态为fulfilled(rejected同理)
const p = new Promise((resolve, reject)=>{
// 直接改变状态和值
resolve(1);
})
// 调用then的时候Promise状态为fulfilled,所以直接将回调放入异步任务队列执行
const p1 = p.then(res=>{
console.log(res); // 1
});
- 调用then的时候,还没有触发resolve是未决状态,Promise状态为pending
const p = new Promise((resolve, reject)=>{
// 一秒钟后将状态改为fulfilled,结果改为1
setTimeout(()=>{
resolve(1);
}, 1000);
})
// 此时调用then的时候为pending状态,将回调加入待执行队列onFulfilledCallbacks中
// 当上面resolve过了一秒执行后,才会将下面的回调放入异步队列中执行,并可以拿到resolve的结果
const p1 = p.then(res=>{
console.log(res); // 1
return res * 10;
});
p1.then(res=>{
// 拿到上面then回调的返回值
console.log(res); // 10
});
- 调用then的时候Promise状态为
rejected
const p = new Promise((resolve, reject)=>{
reject(1);
})
// promise状态为rejected,将then的第二个回调函数放到异步任务队列执行
// then函数的两个回调执行过程中只要不报错,执行完都会将promise状态变为fulfilled
const p1 = p.then(res=>{
console.log(res); // 第一个回调函数不执行
return res * 10;
}, err=>{
console.log(err); // 1
return err * 100;
});
// p1的状态是fulfilled,值是上面onRejected回调的返回值
p1.then(res=>{
// 拿到上面then回调的返回值
console.log(res); // 100
});
- then回调返回一个新的Promise np,会在np的状态变为已决状态后,才会改变当前promise的状态,触发then回调执行。
const p = new Promise((resolve, reject)=>{
resolve(1);
})
const p1 = p.then(res=>{
console.log(res); // 1
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('promise resolve')
}, 1000);
});
});
// 等待返回的promise变为已决状态(一秒后执行回调)
p1.then(res=>{
console.log(res); // promise resolve
});
- 当then回调没有值的时候,会透传下去。
const p = new Promise((resolve, reject)=>{
resolve(1);
})
// then回调为空
const p1 = p.then(null, null);
// 拿到透传下来的结果
p1.then(res=>{
console.log(res); // 1
});
Promise then链实现
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const PENDING = 'pending';
// onFulfilled回调等待队列
+ this.onFulfilledCallbacks = [];
// onRejected回调等待队列
+ this.onRejectedCallbacks = [];
class MyPromise {
constructor(executor) {
this.PromiseState = PENDING;
this.PromiseResult = undefined;
const run = (state, result) => {
if (this.PromiseState !== PENDING) return;
this.PromiseState = state;
this.PromiseResult = result;
// 调用已决状态后,触发对应的待执行回调执行
+ setTimeout(() => {
+ if (state === FULFILLED)
+ this.onFulfilledCallbacks.forEach(callback => callback(this.PromiseResult));
+ else
+ this.onRejectedCallbacks.forEach(callback => callback(this.PromiseResult));
+ });
};
const resolve = (result) => {
if (this.PromiseState !== PENDING) return;
run(FULFILLED, result);
};
const reject = (err) => {
if (this.PromiseState !== PENDING) return;
run(REJECTED, err);
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
resolvePromise(x, resolve, reject) {
// 如果x是个对象,则x可能是promise对象
if (x && typeof x === 'object') {
const then = x.then;
try {
// x是一个promise实例
if (typeof then === 'function') {
// 手动添加onFulfilled和onRejected函数,后续继续递归调用resolvePromise处理
then.call(x, function (y) {
resolvePromise(y, resolve, reject);
}, function (r) {
reject(r);
});
}
} catch (error) {
reject(error);
}
}
// 普通值,直接调用resolve(x),将then返回的promise状态改为已决
else {
resolve(x);
}
}
then(onFulfilled, onRejected) {
// 如果onFulfilled、onRejected为空,赋值默认值透传下去
if (typeof onFulfilled !== 'function') onFulfilled = value => value;
if (typeof onRejected !== 'function') onRejected = err => throw err;
// 返回一个新的promise
return new MyPromise((resolve, reject) => {
// 如果状态为fulfilled,将onFulfilled放入异步任务队列执行
if (this.PromiseState === FULFILLED) {
setTimeout(() => {
try {
// 拿到onFulfilled的返回值
const x = onFulfilled(this.PromiseResult);
// x可能是普通值或者是promise实例,交给resolvePromise处理
// 会调用新promise的resolve将状态改为已决,并传入上面的返回值
this.resolvePromise(x, resolve, reject);
} catch (error) {
// 执行过程中发生错误,直接reject
reject(error);
}
});
}
// 如果状态为rejected,将onRejected放入异步任务队列执行
else if (this.PromiseState === REJECTED) {
setTimeout(() => {
try {
const err = onRejected(this.PromiseResult);
// 拿到onRejected返回值,继续处理,如果没有发生错误,最终还会调用resolve将状态变为fulfilled
this.resolvePromise(err, resolve, reject);
} catch (error) {
reject(error);
}
});
}
// 如果状态为pending,将onFulfilled和onRejected处理逻辑放到对应的等待队列中
else {
onFulfilledCallbacks.push(promiseResult => {
setTimeout(() => {
try {
const result = onFulfilled(promiseResult);
this.resolvePromise(result, resolve, reject);
} catch (error) {
reject(error);
}
});
});
onRejectedCallbacks.push(promiseResult => {
setTimeout(() => {
try {
const result = onRejected(promiseResult);
this.resolvePromise(result, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(fn) {
// 执行回调,穿透到下一个then/catch
return this.then(data => {
return Promise.resolve(fn()).then(() => data);
}, err => {
return Promise.resolve(fn()).then(() => {
throw Error(err);
});
});
}
}
Promise其他核心方法
Promise.resolve & Promise.reject
Promise.resolve
Promise.resolve = function (value) {
return new Promise(function (resolve) {
resolve(value);
});
};
Promise.reject
Promise.reject = function (reason) {
return new Promise(function (_, reject) {
reject(reason);
});
};
Promise.all
Promise.all
所有promise实例都是fulfilled状态,返回的promise才变为fulfilled状态,并将所有实例的解决作为数组传递给返回的promise结果中,其中一个为rejected状态,则返回的promise状态也变成rejected
Promise.all = function (promiseArr) {
return new Promise(function (resolve, reject) {
// 记录当前处理了几个实例
let index = 0,
// 结果数组
ret = [];
// 循环遍历promise实例数组
for (let i = 0; i < promiseArr.length; i++) {
// 取出每一个promise实例
let proItem = promiseArr[i];
// 如果元素不是promise对象,直接将对应数组位置赋值为当前值,index记录加一
if (!(proItem instanceof Promise)) {
index++;
// 为了保证结果的稳定性,利用i向ret里面添加结果
ret[i] = proItem;
if (index === promiseArr.length) {
// 将最终的结果数组resolve
resolve(ret);
}
return;
}
// 当前proItem是promise实例,调用then方法获取resolve/reject处理结果
proItem.then(function (result) {
index++;
// 将获取的结果赋值给数组对应位置
ret[i] = result;
// 如果index记录等于promise实例数组长度,说明所有promise实例已经处理完毕
if (index === promiseArr.length) {
// 将最终的结果数组resolve
resolve(ret);
}
}).catch(function (err) {
// 只要有一个失败就整体失败
reject(err);
});
}
});
};
Promise.race
promise参数数组实例有一个变为已决状态,返回的promise则变成和已决实例相同的状态
Promise.race = function (promiseArr) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promiseArr.length; i++) {
let proItem = promiseArr[i];
if (!(proItem instanceof Promise)) {
resolve(proItem);
} else {
// 是promise实例
proItem.then(result => {
resolve(result);
}, err => {
reject(err);
});
}
}
});
};