本文介绍如何利用 ES6 Promise 封装浏览器原生 FileReader API,将基于回调的事件驱动模型转换为链式调用的异步流程。通过示例展示如何封装文件读取逻辑,处理成功与失败状态,并实现链式操作以简化代码结构。
Promise 异步处理概述
在处理异步任务时,Promise 是极佳的选择,它能有效减少代码嵌套层级,提升可读性并使逻辑更加清晰。ES6 已将 Promise 纳入标准规范,jQuery 3.0 也通过修改实现以贴近该规范(参见 3.0 发布公告)。虽然部分新增 API 如 .fetch() 原生支持 “thenable”,但大多数传统 API 仍依赖回调函数。通过重新封装这些 API,我们可以避免嵌套陷阱,享受 Promise 带来的便利。
Promise 基本用法
Promise 的一般用法 首先了解 Promise 的基本使用方式。
// 声明 Promise 对象
var p = new Promise(function (resolve, reject) {
// 不管啥时候,该执行then了,就调用 resolve
setTimeout(function () {
resolve(1);
}, 5000);
// 或者不管啥问题,就调用 reject
if (somethingWrong) {
reject('2');
}
});
// 使用 Promise 对象
p.then(function (num) {
// 对应上面的 resolve
console.log(num); // 1
}, function (num) {
// 对应上面的 reject
console.log(num); // 2
});
Promise 的驱动模型相对简单:任何操作均可假定为两种结果,成功或失败。只需在适当时机调用相应的程序,进入后续步骤即可。.then() 方法顾名思义表示“下一步”,当先前的 Promise 产生结果——即调用 resolve 或 reject——后,便会启动对应的处理函数。
Promise 实例一旦创建便会立即执行,判定结果需由开发者自行决定,例如加载成功或满足特定条件等。通过串联 .then() 可以执行一系列操作。每次调用 .then() 都会生成一个新的 Promise 实例,该实例会等待前一个实例状态改变后再开始执行。
封装 FileReader 实现
封装 FileReader 接下来进行封装。思路十分简单:FileReader 除了提供多种 read 方法外,还包含几个事件钩子,其中 onerror 和 onload 显然可作为判断任务是否完成的依据。若加载成功,则需要获取文件内容,因此将文件或文件内容传递至下一步也是必要的。
最终完成的代码如下:
function reader (file, options) {
options = options || {};
return new Promise(function (resolve, reject) {
let reader = new FileReader();
reader.onload = function () {
resolve(reader);
};
reader.onerror = reject;
if (options.accept && !new RegExp(options.accept).test(file.type)) {
reject({
code: 1,
msg: 'wrong file type'
});
}
if (!file.type || /^text\//i.test(file.type)) {
reader.readAsText(file);
} else {
reader.readAsDataURL(file);
}
});
}
为了使其真正具备实用价值,代码中还包含了一些验证文件类型的操作,但这与本文主旨无关,此处略过。这段代码的核心在于创建一个 Promise 对象,等待 FileReader 读取完成后调用 resolve 方法,或在出现问题时调用 reject 方法。
封装函数的使用与链式调用
使用刚才封装好的函数 接下来便可在项目中直接使用了:
reader(file)
.then(function (reader) {
console.log(reader.result);
})
.catch(function (error) {
console.log(error);
});
.then() 支持两个参数,第一个参数在 Promise 成功时启动,第二个参数则在失败时启动。使用 .catch() 也能实现相同的效果。Promise 的优势除了提升代码可读性外,返回的 Promise 对象还可以任意传递,继续进行链式调用,具有极大的扩展空间。
继续 .then() 因此,我们可以串联更多操作(原本计划编写断点续传功能,后续再讨论):
全选复制放进笔记reader(file)
.then(function (reader) {
return new Promise(function (resolve, reject) {
// 就随便暂停个5秒吧……
setTimeout(function () {
resolve(reader.result);
}, 5000);
});
})
.then(function (content) {
console.log(content);
});
以上就是本文的全部内容,希望对大家的学习有所帮助。