异步
Promise
一个【表示异步操作的对象】,它会完成一个在未来才会知道结果或失败的操作;
获取结果后,可以使用 then、catch 方法执行回调,处理结果;
Promise 的核心是:将原来的回调函数,改写成链式调用的形式;
Promise 状态
pending;等待结果,从 p 到 f/r 是不可逆的改变;
fulfilled;从 p 到 f,操作成功,触发 then 方法;
rejected;从 p 到 r,操作失败,触发 catch 方法;
异常传递
沿 Promise 链传递,直到被 catch 捕获,它会捕获到所有未处理的异常;
延迟绑定(特性)
执行器函数立即执行,但是 then/catch 的回调函数是不确定执行时机的,需要等待 promise 的状态改变;
延迟绑定的意义在于:一个 promise 可以被不同的模块共享,在需要的时机绑定回调并执行,它的创建和使用可以在不同的时机或位置。
代码穿透(特性)
Promise 的异常会冒泡到最外层的代码(只能通过 catch 处理错误);
then 方法只会通过 reject 或 resolve 的 promise 持续向后传递,直到被 catch 捕获,所以要在 Promise 的最后加上 catch 方法,捕获可能出现的错误。
Promise.all
处理多个异步操作,把多个 Promise 包装成一个 Promise,最终状态由所有传入的 Promise 决定,当所有 Promise 都 resolve 时才 resolve,有任一 reject 就直接 reject;
场景(同时处理多个请求,然后统一处理结果)
页面初始化;分批上传文件;
Promise.race
处理多个异步操作,最终状态由第一个完成的 Promise 决定。任一 Promise 被 reject 就直接 reject;
场景:超时处理,竞态条件(查询缓存或网络);
中断链式调用的方式
throw new Error
return Promise.reject("error")
return 123(非 promise 值)
用于:参数验证失败、登录验证失效
Promise 为什么引入微任务
Promise 的 resolve 和 reject 回调采用了延时绑定机制;
微任务可以保证 Promise 的回调函数可以优先执行,因为相对定时器而言,微任务通常是比较关键的任务;
Async / Await
async 函数会返回一个 promise 对象,await 是 then 方法的语法糖;
async、await 不改变 promise 的本质,但会让异步代码看起来更像同步代码;
await 会暂停代码在该语句上;
await 暂停执行原理
await 后面的表达式会先执行一次,返回一个 promise 对象;
然后暂停当前 async 函数的执行,等待 promise 状态;
一旦 promise 状态改变,异步操作完成,await 就会回复到 async 函数的执行,确保后面的代码执行时已经获取到结果了;
定义多个异步任务,按顺序打印数据
// async 和 await 方法
async function sqAsyncTasks(tasks) {
for (const task of tasks) {
const result = await task();
}
}
sqAsyncTasks([asyncTask1, asyncTask2, asyncTask3]);
// reduce 和 promise 方法
function seqAsyncTasks(tasks) {
return tasks.reduce((prevPromise, currentTask) => {
return prevPromise.then(() => {
return currentTask().then((result) => {
// todo
});
});
}, Promise.resolve());
}
实现一个 Promise
function myPromise(execute) {
var onResolve_ = null;
var onReject_ = null;
// 注册成功和失败的回调函数,this.then 所以可以实现延迟执行
// onResolve 先赋值给 onResolve_,但是要等到异步操作结束后,在 resolve 中执行
this.then = function (onResolve, onReject) {
onResolve_ = onResolve;
onReject_ = onReject;
};
// setTimeout 用来模拟异步操作的延迟完成,因为 resolve 和 reject 是在一段时间后调用的
function resolve() {
setTimeout(() => {
onReolve_(value);
});
}
function reject(error) {
setTimeout(() => {
onReject_(error);
});
}
execute(resolve, reject); // execute 里执行异步操作
}