回调函数
- 回调函数分为两种类型,分别为同步回调与异步回调;
- 同步回调:会立即执行,完全执行完了才结束,不会放入回调队列中;
- 异步回调:不会立即执行,会放入回调队列中 将来执行;
<script>
//同步回调
const arr = [1, 3, 5];
arr.forEach((item) => {
console.log(item);
});
console.log('forEach(之后)');
//异步回调
setTimeout(() => {
console.log('异步回调');
}, 0);
console.log('setTimeout()之后');
</script>
- 遍历回调属于同步回调,立即执行;
- 定时器中的回调属于异步回调,不会立即执行;
常见的内置错误
- Error:所有错误的父类型;
- ReferenceError:引用变量不存在;
- TypeError:数据类型不正确;
- RangeError:数据值不在其所允许的范围之内;
- SyntaxError:语法错误;
<script>
//1.ReferenceError:引用变量不存在
console.log(a); //ReferenceError: a is not defined
//2.TypeError:数据类型不正确
let b = {};
b.xxx(); //TypeError: b.xxx is not a function
//3.RangeError:数据值不在其所允许的范围之内
function func() {
func()
}
func(); //RangeError: Maximum call stack size exceeded
//4.SyntaxError:语法错误
const c = """"; //SyntaxError: Unexpected string
</script>
错误的处理
- 错误处理的方式有两种:捕获错误和抛出错误;
- 捕获错误:
try ... catch
;
<script>
try {
let b;
console.log(b.xxx);
} catch (error) {
console.log(error.message);
console.log(error.stack);
}
console.log('出错之后');
</script>
- 抛出错误:
throw error
,错误由调用者进行处理;
<script>
function something() {
if (Date.now % 2 === 1) {
console.log('当前时间为奇数,可以执行任务');
} else {
throw new Error('当前时间为偶数,抛出异常 无法执行任务');
}
}
//抛出错误之后 调用者来处理了
try {
something();
} catch (error) {
alert(error.message);
}
</script>
Promise
- Promise是JS中进行异步编程的一种解决方案;
- 从语法上来说,Promise是一个构造函数;
- 从功能上来说,Promise对象用来封装一个异步操作并可以获取其结果;
Promise的状态
- 初始化状态为pending;
- pending变为resolved;
- pending变为reiected;
- 一个Promise对象,其状态只能改变一次,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般称为value,失败的结果数据一般称为reason;
- Promise执行的基本流程如下:
image.png
Promise的基本使用
- 首先创建Promise实例对象;
- 在执行器中执行异步任务;
- 获取异步任务的处理结果;
<script>
//1.创建Promise实例对象
const promise = new Promise((resolve, reject) => {
//2.在执行器中 执行异步任务
setTimeout(() => {
const time = Date.now();
if (time % 2 === 0) {
resolve('成功的数据,time = ' + time);
} else {
reject('失败的数据,time = ' + time);
}
})
}, 1000);
//3.处理异步任务的结果
promise.then(
value => {
console.log('成功的回调', value);
},
reason => {
console.log('失败的回调', reason);
}
)
</script>
Promise的优点
- 首先Promise指定回调函数的方式更加灵活;
- 纯回调函数形式,指定回调函数必须在异步任务执行之前;
- Promise形式,指定回调函数在执行异步任务前后均可以,更加灵活;
- 其次Promise支持链式调用,可以解决回调地狱问题,终极解决方案为:async/await;
<script>
//1.使用纯回调函数的形式
function successCallback(result) {
console.log('成功结果:' + result);
}
function failCallback(error) {
console.log('失败原因:' + error);
}
//必须在异步任务执行之前 指定回调函数
createAudioFileAsync(audioSettings, successCallback, failCallback);
//2.使用Promise的形式
const promise = createAudioFileAsync(audioSettings);
//指定回调函数 非常灵活 可以在异步任务执行前后 指定均可以
promise.then(successCallback, failCallback);
</script>
<script>
function failCallback(error) {
console.log('失败原因:' + error);
}
//案例:三个请求 依次依赖
//1.回调嵌套地狱 下一层函数的调用 依赖上一层函数的结果
doFirstThing(function (result) {
doSecondThing(result, function (newResult) {
doThirdThing(newResult, function (lastResult) {
console.log('lastResult =', lastResult);
}, failCallback);
}, failCallback);
}, failCallback);
//2.使用Promise链式调用 解决回调地狱问题
doFirstThing()
.then(function (result) {
return doSecondThing(result);
})
.then(function (newResult) {
return doThirdThing(newResult);
})
.then(function (lastResult) {
console.log('lastResult =', lastResult);
})
.catch(failCallback);
//3.回调地狱的终极解决方案: async/await
async function request() {
try {
const result = await doFirstThing();
const newResult = await doSecondThing(result);
const lastResult = await doThirdThing(newResult);
console.log('lastResult =', lastResult);
} catch (error) {
failCallback(error);
}
}
</script>
Promise常见API
image.png
<script>
//then value --> 异步执行成功
//catch reason --> 异步执行失败
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('111111');
}, 5000);
}).then((value) => {
console.log(value);
}).catch((reason) => {
console.log(reason);
});
//Promise.resolve() 或者 Promise.reject() 均会返回一个Promise实例对象
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = Promise.resolve(2);
const p3 = Promise.reject(3);
p1.then(value => console.log(value));
p2.then(value => console.log(value));
p3.catch(reason => console.log(reason));
//pAll 数组中异步操作只要有一个失败 就直接失败
const pAll = Promise.all([p1, p2, p3]);
pAll.then(
value => {
console.log('all onResolve()', value);
},
reason => {
console.log('all onReject()', reason);
}
)
//异步操作数组中的 某个异步操作最先返回结果 就是pRace的最终结果
const pRace = Promise.race([p1, p2, p3]);
pRace.then(
value => {
console.log('all onResolve()', value);
},
reason => {
console.log('all onReject()', reason);
}
)
</script>
-
Promise.resolve(value)
:返回值为一个新的promise,参数value值可以是promise,可以是非promise- 当value为非promise时,那么新promise的状态结果为resolve成功;
- 当value为promise时,那么新promise的状态结果为value的状态结果;
-
Promise.reject(value)
:返回值为一个新的promise,其状态结果为reject失败; -
Promise.all(promise数组)
:返回值为一个新的promise,其状态结果为:- 当promise数组中存在一个状态失败的,那么新promise的状态结果为失败;
- 当promise数组中都成功,那么新promise的状态结果为一个数组集合值;
-
Promise.race(promise数组)
:返回值为一个新的promise,其状态结果为:promise数组中先返回状态结果的promise的状态结果;
Promise的使用注意事项
- promise的状态改变分为三种情况:
- 执行成功resolve(value),pending --> resolve;
- 执行失败reject(reason),pending --> reject;
- 抛出异常 throw error,pending --> reject;
- 一个promise对象,可以指定多个状态回调,且多个状态回调都会执行;
<script>
const p = new Promise((resolve, reject) => {
// resolve(1); //成功 pending --> resolve
// reject(2); //失败 pending --> reject
throw new Error('报错了'); //抛出异常 pending --> reject
});
//promise指定多个回调 都会调用
p.then(
value => { },
reason => { console.log('reason', reason) }
);
p.then(
value => { },
reason => { console.log('reason', reason) }
);
</script>
- 如何控制
改变promise状态
和指定回调函数
的先后顺序?- 常规情况下:先指定回调函数,再改变promise状态,执行回调函数;
<script>
//先指定回调函数 再改变状态
new Promise((resolve, reject) => {
//2.异步执行 再改变状态(同时指定数据) 异步执行下面指定的回调函数
setTimeout(() => {
resolve(1);
})
}).then(
//1.先指定回调函数,promise会保存当前指定的回调函数
value => { console.log('value', value) },
reason => { console.log('reason', reason) }
);
//先指定回调函数 再改变状态
new Promise((resolve, reject) => {
//1.先改变状态(同时指定数据)
resolve(1);
}).then(
//2.后指定回调函数 异步执行回调函数
value => { console.log('value', value) },
reason => { console.log('reason', reason) }
);
//先指定回调函数 再改变状态
const p = new Promise((resolve, reject) => {
//2.异步执行 再改变状态(同时指定数据) 异步执行下面指定的回调函数
setTimeout(() => {
resolve(1);
})
});
setTimeout(() => {
p.then(
//1.先指定回调函数,promise会保存当前指定的回调函数
value => { console.log('value', value) },
reason => { console.log('reason', reason) }
);
}, 2000);
</script>
- promise.then()会返回一个新的promise对象,此promise的状态结果由什么决定?
- 由then()指定的回调函数执行的结果决定;
- then()指定的回调函数,没有返回值,则新的promise对象状态变成resolve,value值为undefined;
- then()指定的回调函数,返回非promise的任意值value,则新的promise对象状态变成resolve,会执行成功的回调,且获取value值;
- then()指定的回调函数,抛出异常,则新的promise对象状态变成reject,会执行失败的回调;
- then()指定的回调函数,返回另一个新的promise,则此promise的状态结果会成为新promise的状态结果;
<script>
new Promise((resolve, reject) => {
resolve(1);
}).then(
value => { console.log('onResolved1', value) },
reason => {
console.log('onRejected1', reason)
// return 2;
// return Promise.resolve(3);
// return Promise.reject(4);
// throw 5;
}
).then(
value => { console.log('onResolved2', value) },
reason => { console.log('onRejected2', reason) }
);
</script>
- promise如何串联多个操作任务?
- promise.then()可返回一个新的promise;
- promise.then()的链式调用可串联多个同步/异步任务;
<script>
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行异步任务1');
resolve(1);
}, 1000);
}).then(
value => {
console.log('异步任务1的结果:', value);
console.log('执行同步任务2');
return 2;
},
reason => {
console.log('onRejected1', reason);
}
).then(
value => {
console.log('同步任务2的结果:', value);
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行异步任务3');
resolve(3);
}, 1000);
})
},
reason => {
console.log('onRejected2', reason);
}
).then(
value => {
console.log('异步任务3的结果:', value);
}
);
</script>
image.png
- promise的异常传透:
- 当使用promise的then链式调用时,可以再最后指定失败的回调;
- 前面任何操作出现了异常,都会传到最后失败的回调;
<script>
new Promise((resolve, reject) => {
reject(1);
}).then(
value => {
console.log('onResolve1:', value);
return 2;
},
// reason => {
// throw reason;
// }
).then(
value => {
console.log('onResolve2:', value);
return 3;
},
// reason => {
// throw reason;
// }
).then(
value => {
console.log('onResolve3:', value);
},
// reason => {
// throw reason;
// }
).catch(
reason => {
console.log('onReject: ', reason);
}
)
</script>
- . catch失败处理,上面没有失败的处理,就会执行. catch中的代码,
没有失败的处理
等价于reason => { throw reason }
,抛出了异常,然后一直往下传递异常,最终会执行. catch中的代码; - 如何中断promise链?
- 当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数;
- 方案:在回调函数中返回一个pending状态的promise对象;
<script>
new Promise((resolve, reject) => {
reject(1);
}).then(
value => {
console.log('onResolve1:', value);
return 2;
},
).then(
value => {
console.log('onResolve2:', value);
return 3;
},
).then(
value => {
console.log('onResolve3:', value);
},
).catch(
reason => {
console.log('onReject1: ', reason);
// throw reason;
// return Promise.reject(3);
//返回一个pending状态的promise
//下面的回调函数不会执行
return new Promise(() => { });
}
).then(
value => {
console.log('onResolve3:', value);
},
reason => {
console.log('onReject2: ', reason);
}
)
</script>
- 注意.catch()函数也会返回一个新的promise对象;
自定义Promise
- 函数对象,代码如下:
//自定义Promise函数模块
(function (window) {
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
//Promise的构造函数
function Promise(excutor) {
const self = this;
//promise状态 初始化为pending
self.status = PENDING;
//promise异步操作的结果数据
self.data = undefined;
//promise的回调函数
self.callbacks = [];
function resolve(value) {
if (self.status !== PENDING) {
return;
}
//将状态改成resolved
self.status = RESOLVED;
self.data = value;
//若存在callback回调函数,则立即异步执行回调函数
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(element => {
element.onResolved(value);
});
});
}
}
function reject(reason) {
if (self.status !== PENDING) {
return;
}
self.status = REJECTED;
self.data = reason;
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(element => {
element.onRejected(reason);
});
});
}
}
try {
excutor(resolve, reject);
} catch (error) {
//捕获异常了 执行reject方法 状态成为rejected
reject(error);
}
}
//Promise原型上的函数方法
Promise.prototype.then = function (onResolved, onRejected) {
//当前promise对象
const self = this;
onResolved = typeof onResolved === 'function' ? onResolved : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
//返回一个新的promise对象
return new Promise((resolve, reject) => {
//处理函数
function handle(callback) {
//新promise对象的状态结果 由当前promise对象的状态结果决定
//1.当前promise结果为抛出异常,则新的promise结果为失败,值为异常error
//2.当前promise结果为非promise,则新的promise结果为成功,值为异常value
//3.当前promise结果为promise,则新的promise结果为promise的结果
try {
const result = callback(self.data);
if (result instanceof Promise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
}
if (this.status === RESOLVED) {
//立即异步执行 当前promise对象 成功的回调函数
setTimeout(() => {
handle(onResolved);
});
} else if (self.status === REJECTED) {
setTimeout(() => {
handle(onRejected);
});
} else {
//将成功和失败的回调函数保存到callbacks容器中
self.callbacks.push({
onResolved(value) {
handle(onResolved);
},
onRejected(reason) {
handle(onRejected);
}
});
}
});
if (self.status === PENDING) {
//保存回调方法 到一个对象中
self.callbacks.push({
onResolved,
onRejected
})
} else if (self.status === RESOLVED) {
setTimeout(() => {
resolve(self.data)
});
} else {
setTimeout(() => {
reject(self.data);
});
}
}
Promise.prototype.catch = function (onRejected) {
Promise.prototype.then(undefined, onRejected);
}
//Promise函数对象方法
Promise.resolve = function (value) {
//返回一个状态结果为 成功或失败的promise对象
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(resolve, reject);
} else {
resolve(value);
}
})
}
Promise.reject = function (reason) {
//返回一个状态结果为失败的promise对象
return new Promise((resolve, reject) => {
reject(reason);
})
}
Promise.all = function (promises) {
//保存所有成功的value的数组
const values = new Array(promises.length);
//记录成功promise的数量
let resloveCount = 0;
return new Promise((reslove, reject) => {
promises.forEach((p, index) => {
p.then(
value => {
resloveCount++;
values[index] = value;
if (resloveCount === promises.length) {
reslove(values);
}
},
reason => {
reject(reason);
}
)
});
});
}
Promise.race = function (promises) {
return new Promise((resove, reject) => {
promises.forEach((p, index) => {
p.then(
value => {
resove(value);
},
reason => {
reject(reason);
}
)
});
});
}
//向外暴露Promise的构造函数
window.Promise = Promise;
})(window)
- 类Class对象,代码如下:
//自定义Promise函数模块
(function (window) {
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
class Promise {
constructor(excutor) {
//Promise的构造函数
const self = this;
//promise状态 初始化为pending
self.status = PENDING;
//promise异步操作的结果数据
self.data = undefined;
//promise的回调函数
self.callbacks = [];
function resolve(value) {
if (self.status !== PENDING) {
return;
}
//将状态改成resolved
self.status = RESOLVED;
self.data = value;
//若存在callback回调函数,则立即异步执行回调函数
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(element => {
element.onResolved(value);
});
});
}
}
function reject(reason) {
if (self.status !== PENDING) {
return;
}
self.status = REJECTED;
self.data = reason;
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(element => {
element.onRejected(reason);
});
});
}
}
try {
excutor(resolve, reject);
} catch (error) {
//捕获异常了 执行reject方法 状态成为rejected
reject(error);
}
}
//Promise原型上的函数方法
then(onResolved, onRejected) {
//当前promise对象
const self = this;
onResolved = typeof onResolved === 'function' ? onResolved : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
//返回一个新的promise对象
return new Promise((resolve, reject) => {
//处理函数
function handle(callback) {
//新promise对象的状态结果 由当前promise对象的状态结果决定
//1.当前promise结果为抛出异常,则新的promise结果为失败,值为异常error
//2.当前promise结果为非promise,则新的promise结果为成功,值为异常value
//3.当前promise结果为promise,则新的promise结果为promise的结果
try {
const result = callback(self.data);
if (result instanceof Promise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
}
if (this.status === RESOLVED) {
//立即异步执行 当前promise对象 成功的回调函数
setTimeout(() => {
handle(onResolved);
});
} else if (self.status === REJECTED) {
setTimeout(() => {
handle(onRejected);
});
} else {
//将成功和失败的回调函数保存到callbacks容器中
self.callbacks.push({
onResolved(value) {
handle(onResolved);
},
onRejected(reason) {
handle(onRejected);
}
});
}
});
if (self.status === PENDING) {
//保存回调方法 到一个对象中
self.callbacks.push({
onResolved,
onRejected
})
} else if (self.status === RESOLVED) {
setTimeout(() => {
resolve(self.data)
});
} else {
setTimeout(() => {
reject(self.data);
});
}
}
catch(onRejected) {
Promise.prototype.then(undefined, onRejected);
}
//Promise函数对象方法
static resolve(value) {
//返回一个状态结果为 成功或失败的promise对象
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(resolve, reject);
} else {
resolve(value);
}
})
}
static reject(reason) {
//返回一个状态结果为失败的promise对象
return new Promise((resolve, reject) => {
reject(reason);
})
}
static all(promises) {
//保存所有成功的value的数组
const values = new Array(promises.length);
//记录成功promise的数量
let resloveCount = 0;
return new Promise((reslove, reject) => {
promises.forEach((p, index) => {
p.then(
value => {
resloveCount++;
values[index] = value;
if (resloveCount === promises.length) {
reslove(values);
}
},
reason => {
reject(reason);
}
)
});
});
}
static race(promises) {
return new Promise((resove, reject) => {
promises.forEach((p, index) => {
p.then(
value => {
resove(value);
},
reason => {
reject(reason);
}
)
});
});
}
}
//向外暴露Promise的构造函数
window.Promise = Promise;
}
)(window)
async与await
-
async 函数
:函数的返回值是promise对象,promise对象的状态结果由async函数执行的返回值决定;
<script>
async function test() {
// return 1;
throw 2;
// return Promise.resolve(3);
}
//返回结果为promise对象
const result = test();
result.then(
value => {
console.log('onResolved()',value);
},
reason => {
console.log('onRejected()',reason);
}
);
</script>
-
await 表达式
:一般情况下await右侧的表达式为promise对象,也可以是其他的值;‘- 若表达式是promise对象,await返回的是promise成功的值;
- 若表达式是其他值,直接将此值作为await的返回值;
- 注意事项:
- await必须写在async函数中,但async函数中可以没有await;
- 如果await的promise失败了,就会抛出异常,需要通过try...catch来捕获处理;
<script>
function test2() {
return new Promise((reslove, reject) => {
setTimeout(() => {
// reslove(5);
reject(6);
}, 1000);
})
}
async function test3() {
try {
//表达式是promise对象,await返回的是promise成功的值
const value = await test2();
console.log(value);
} catch (error) {
//await的promise失败了,就会抛出异常,需要通过try...catch来捕获处理
console.log('得到失败的结果', error);
}
//表达式是其他值,直接将此值作为await的返回值
const value = test4();
console.log(value);
}
function test4() {
return 6;
}
test3();
</script>
异步执行的宏队列与微队列
- 在JS中 存储 待执行回调函数的 队列 有两种特定的队列,分别为宏队列和微队列;
- 宏队列:用来保存待执行的宏任务回调函数,例如定时器回调,DOM事件回调,ajax回调;
- 微队列:用来保存待执行的微任务回调函数,例如Promise回调,MutationObserver回调;
- JS执行时会区分 这两个队列;
- JS引擎首先必须先执行完所有的初始化同步任务代码;
-
每次准备取出第一个宏任务执行前,都要将所有微任务一个一个取出来执行
;
<script>
setTimeout(() => {
//回调函数 放入宏队列
console.log('timeout callback1()');
Promise.resolve(3).then(
value => {
//回调函数 放入微队列
console.log('Promise onResolved3()', value);
}
)
}, 0);
setTimeout(() => {
//回调函数 放入宏队列
console.log('timeout callback2()');
}, 0);
Promise.resolve(1).then(
value => {
//回调函数 放入微队列
console.log('Promise onResolved1()', value);
}
)
Promise.resolve(2).then(
value => {
//回调函数 放入微队列
console.log('Promise onResolved2()', value);
}
)
</script>
image.png