异步编程
常见的异步编程方式有 回调函数,事件发布/订阅,Promise,generator函数,async函数
这篇笔记是记录一下学习promise的过程,回调和事件发布/订阅,就不记录笔记了
promise
Promise/A+规范
1 术语
promise :一个有then方法的对象或者函数,行为符合本规范
thenable :一个对象或者函数,拥有then方法
值,value :任何javascript 的合法值
异常,exception :throw语句抛出的值
拒绝原因,reason :一个标示promise被拒绝原因的值
术语的含义下文会慢慢分析
2 要求
promise状态
pending
等待状态,可以转换为fulfilled状态或者rejected状态 fulfilled 完成状态,不可以转换成其他的状态,且必须带有value,且这个值不能被改变 rejected 拒绝状态,不可以转换成其他的状态,且必须带有reason,且这个值不能被改变
then方法
then方法的参数
一个promise必须提供then方法一个参数才能获取其value或者reason
then方法接受两个参数,promise.then(onFulfilled,onRejected)
onFulfilled在promise完成之后调用,onRejected在promise被拒绝之后调用,并且只能调用一次 ,因为上文状态说明只能状态一旦改变,就无法再次改变,所以意味着两种方法只能调用一次
then方法的调用
then方法可以被同一个promise调用多次,onFulfilled和onReject会按照注册的顺序依次调用
then方法的返回值
then方法必须返回一个promise
let promise2=promise1.then(onFulfilled, onRejected)
promise2的值和状态
onFulfilled不是函数则忽略之
onRejected不是函数则忽略之
onRejected或者onFulfilled return了一个值X,则进入解析过程,promise解析过程 可以提取一个抽象模型resolve(promise,x) x是then的参数返回的值,
1 如果promise和x指向同一个值
2 如果x是一个promise
3 如果x是一个对象或者函数
4 如果x不是对象也不是函数
下面结合代码分析解析过程
function resolve(promise, x) {
//如果promise和x指向同一个值,则以typeError为因,拒绝执行promise
if (x === promise) {
return reject(promise, new TypeError("cant be the same"));
}
//如果x是一个promise
if (isPromise(x)) {
//如果x.state为fulfilled,则将x.value 作为promise的value,并且将promise设为fulfilled状态
if (x.state === "fulfilled") {
return fulfill(promise, x.value);
}
//x.state是rejected ,则将x.reason作为promise的值,并且将promise设为rejected状态
if (x.state === "rejected") {
return reject(promise, x.reason);
}
//如果x.state是pending 状态,则需要等待state变化之后,才能决定promise的state和value
//所以需要将promise状态的改变放到then方法的回调里面
if (x.state === "pending") {
return x.then(
() => {
//如果是fulfilled,则再走一遍解析
resolve(promise, x.value);
},
() => {
//如果是rejected,则将x.reason作为promise的值,并且将promise设为rejected状态
reject(promise, x.reason);
}
);
}
} else if (isObject(x) || isFunction(x)) {
//x是一个对象或者函数(thenable)
//为什么术语中会出现一个和promise如此相似的thenable呢?因为在规范出现之前,已经有库实现了类似promise的功能,
//所以为了对这些库兼容,就针对thenable在规范中进行了一个处理
//直接去取x.then
let then;
let isCalled = false;
//如果出现异常,则将抛出的异常作为reason,将promise设置为rejected状态
try {
then = x.value;
} catch (e) {
return reject(promise, e);
}
//如果是一个函数
if (isFunction(then)) {
try {
//以x为this调用then函数
//将两个函数作为onFulfilled和onRejected参数传给then
//then方法在执行的时候,更具不同的情况调用函数
//根据上文可知,要保证函数只被调用一次,因为我们希望thenable是类似promise的
then.call(
x,
function resolvePromise(y) {
if (isCalled) return;
isCalled = true;
resolve(promise, y);
},
function rejectPrommise(r) {
if (isCalled) return;
isCalled = true;
reject(promise, r);
}
);
} catch (e) {
//如果执行过程出错,则需要看一下两个函数是否已经执行过了
//如果执行过了,则说明promise状态已经被改变了,所以不做处理
//如果没有执行过,则将执行时的异常作为reason,将promise设置为rejected状态
if (!isCalled) return reject(promise, e);
}
} else {
//如果then不是function ,则需要将x作为promise一个值,将promise设置为fulfilled状态
return fulfill(promise, x);
}
} else {
//如果x不是对象也不是函数,则将x作为promise一个值,将promise设置为fulfilled状态
return fulfill(promise, x);
}
}
根据规范看一下代码示例
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
// 这个很简单 1 2 4 3
const promise = Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log);
// 这段代码如果刚开始看不懂的话,可以分解一下
const promise1 = Promise.resolve(1); //通过resolve方法将promise1变为fulfilled状态,value为1
const promise2 = promise1.then(2); //onFulfilled不是函数且promise1的state是fulfilled 则promise2的state:fulfilled value:同promise1
const promise3 = promise2.then(Promise.resolve(3)); //同上
const promise4 = promise3.then(console.log); //因为console.log是一个function,其参数为promise3的value
ES6 Promise API
没啥好说的,就简单记录一下
new Promise((resolve,reject)=>{
// resolve() //将promise从pending状态变成 fulfilled
// reject() //将promise从pending状态变成 rejected
})
Promise.resolve(param) 等同于 new Promise((resolve,reject)=>resolve(param))
Promise.reject(reason) 等同于 new Promise((resolve,reject)=>reject(reason))
Promise.all([p1,...,pn]) 输入一组promise返回一个新的promise ,全部promise的状态为fulfilled结果才是fulfilled状态,否则就是rejected状态
Promise.race([p1,...,pn]) 输入一组promise返回一个新的promise,只要有一个promise被fulfilled,结果promise就会状态就会变成fulfilled
Promise.allSettled([p1,...,pn]) 输入一组promise返回一个新的promise,全部promise的状态改变之后结果promise才会使fulfilled状态
promise实例方法
promise.then(onFulfilled,onRejected) promise状态改变之后的回调,返回新的promise对象
promise.catch((reason)=>{}) 等同于promise.then(null,onRejected)
promise.finally((reason)=>{}) 不管promise状态如何都会执行
promise 实践
3S之后亮红灯一次,再过两秒亮绿灯一次,再过一秒亮黄灯一次 ,用promise实现多次交替亮灯的效果
拆解步骤 1多少秒后亮那一盏灯 2顺序亮灯 3循环顺序亮灯
let lightList = [{
color: "red",
second: 3,
}, {
color: "green",
second: 2,
}, {
color: "yellow",
second: 1,
}, ];
//对list进行某种操作
function loopLight(list) {
let promise = Promise.resolve();
list.forEach((item) => {
promise = promise.then(() => {
return lightUp(item.color, item.second);
});
});
}
//亮灯
function lightUp(color, second) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(color);
resolve();
}, second * 1000);
});
}
loopLight(lightList);
上面的代码可以实现第一步和第二步,如果想实现循环亮灯需要对loopLight
进行修改,只要在forEach完成之后,再次运行,下面写上完整代码
let lightList = [{ color: "red", second: 3, }, { color: "green", second: 2, }, { color: "yellow", second: 1, }, ];
function lightUp(color, second) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(color);
resolve();
}, second * 1000);
});
}
function loopLight(list) {
//then方法必须返回一个返回值:Promise
//then 方法可以被同一个Promise 方法调用多次,那么onFulfilled, onRejected会按照注册顺序调用
let promise = Promise.resolve();
list.forEach((item) => {
promise = promise.then(() => {
return lightUp(item.color, item.second);
});
});
promise.then(() => {
loopLight(lightList);
});
}
loopLight(lightList);
这篇笔记到此结束,如果有错误或者更好的方法,希望看到的大佬不吝赐教。