跳到主要内容

异步编程实践

图片异步加载

在处理大量图片时,异步加载可以提升页面性能和用户体验。以下是一个实现图片异步加载的示例:

function loadImage(src) {
return new Promise((resolve, reject) => {
const image = new Image();
image.src = src;
image.onload = () => {
resolve(image);
};
image.onerror = () => {
reject(new Error(`加载图片失败: ${src}`));
};
document.body.appendChild(image);
});
}

loadImage('./favicon.ico')
.then((image) => {
image.style.border = 'solid 6px red';
})
.catch((error) => {
console.error(error);
});

在这个例子中,我创建了一个loadImage函数,它返回一个 Promise。在图片成功加载后,Promise 会被解决,并将图片元素添加到文档中。如果加载失败,Promise 会被拒绝,并输出错误信息。

setTimeout 的使用

setTimeout用于在指定的延迟后执行函数。通过将setTimeout封装在 Promise 中,可以实现更灵活的异步控制。

function delay(ms = 1000) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}

delay(2000)
.then(() => {
console.log(1);
return delay(2000);
})
.then(() => {
console.log(2);
});

在此示例中,delay函数接受一个毫秒数作为参数,并返回一个在指定时间后解决的 Promise。通过链式调用,可以实现顺序的延迟执行。

setInterval 的封装

setInterval可以重复执行一个函数。以下是将setInterval封装为 Promise 的实现:

function interval(delay = 1000, callback) {
return new Promise((resolve) => {
const intervalId = setInterval(() => {
callback(intervalId, resolve);
}, delay);
});
}

interval(2000, (id, resolve) => {
console.log('1');
clearInterval(id);
resolve('2');
})
.then((value) => {
return interval(2000, (id, resolve) => {
console.log(value);
clearInterval(id);
resolve('3');
});
})
.then((value) => {
return interval(2000, (id, resolve) => {
console.log(value);
clearInterval(id);
resolve('4');
});
})
.then((value) => {
interval(2000, (id, resolve) => {
console.log(value);
clearInterval(id);
console.log(resolve('5'));
});
});

通过这种方式,可以控制定时器的启动和停止,同时利用 Promise 链实现顺序执行。

异步加载 JS 脚本

动态加载 JavaScript 脚本有助于按需加载资源,优化页面加载速度。以下是一个异步加载脚本的示例:

function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`加载脚本失败: ${src}`));
document.body.appendChild(script);
});
}

loadScript('./index.js')
.then((script) => {
return loadScript('./index.js');
})
.then(() => {
console.log(1);
})
.catch((error) => {
console.error(error);
});

这个loadScript函数创建一个<script>元素,并在加载完成后解决 Promise。如果加载失败,则拒绝 Promise。这种方法可以确保脚本按顺序加载,并处理可能的错误。

Promise.all 的批量请求

使用Promise.all可以同时发起多个请求,并在所有请求完成后处理结果。然而,如果其中任何一个请求失败,Promise.all会立即拒绝。以下是一个示例:

const promiseA = new Promise((resolve) => {
setTimeout(() => {
resolve('1');
}, 1000);
});

const promiseB = new Promise((resolve) => {
setTimeout(() => {
resolve('2');
}, 1000);
});

Promise.all([promiseA, promiseB])
.then((results) => {
console.log(results);
})
.catch((error) => {
console.error(error);
});

在这个例子中,promiseApromiseB同时执行,Promise.all会在两者都完成后输出结果。如果其中任何一个 Promise 被拒绝,错误将被捕获并输出。

使用 Map 实现 Promise 队列

通过Map可以实现一个简单的 Promise 队列,确保任务按顺序执行。以下是一个示例:

function executeQueue(tasks) {
let sequence = Promise.resolve();
tasks.map((task) => {
sequence = sequence.then(() => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(task);
resolve();
}, 1000);
});
});
});
}

executeQueue([1, 2, 3, 4, 5]);

在此示例中,executeQueue函数接受一个任务数组,每个任务将在前一个任务完成后执行,确保顺序性。

使用 reduce 实现 Promise 队列

reduce方法提供了一种更简洁的方式来实现 Promise 队列。以下是一个示例:

function executeQueue(tasks) {
tasks.reduce((promiseChain, currentTask) => {
return promiseChain.then(() => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(currentTask);
resolve();
}, 1000);
});
});
}, Promise.resolve());
}

executeQueue([1, 2, 3, 4, 5]);

通过reduce,每个任务在前一个任务完成后执行,确保任务按顺序执行。这种方法简洁且易于理解。