异步

面试
异步

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 里执行异步操作
}