# fed-e-task-02-1 **Repository Path**: dadami/fed-e-task-02-1fed-e-task-02-1 ## Basic Information - **Project Name**: fed-e-task-02-1 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-06-15 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #### 杨家馨 Part2 简答题 1 对前端工程化的认识 前端工程化就是将前端开发流程,标准化、规范化、工具化、自动化、简单化。通过规范和工具来提高前端应用质量及开发效率, 最近几年,尤其是在nodejs出现之后,前端越来越受到重视,甚至已经开始抢占了后端的市场。这同时也带来了一个问题,前端的规模越来越大,已经上升到了工程学的问题,如何提高前端工程师的开发效率变得非常重要。这就是前端工程化所要解决的问题。前端工程化是根据业务特点,将前端开发流程规范化,标准化,它包括了开发流程,技术选型,代码规范,构建发布等,用于提升前端工程师的开发效率和代码质量。 - 使用ES6新特性, 兼容有问题 - 使用Less / Sass增强CSS的编程性, 运行环境不支持 - 上线发布需要手动压缩代码和资源文件, 部署过程需要手动上传服务器 - 多人协作开发, 无法硬性同意大家的代码风格 - 从仓库中pull 回来的代码质量无法保证 - 部分功能开发时需要等待后端服务接口提前完成 #### 解决的问题 - 传统语言或语法弊端 - 无法使用模块化 / 组件化 - 重复的机械式工作 - 代码风格统一,质量保证 - 依赖后端接口服务支持 - 整体依赖后端项目 问题2 脚手架意义 - 减少时间,不必从零开始搭建初始项目,提高开发效率。 - 便于多人协作 - 提供项目规范和约定 #### 问题3 实现自定义脚手架 [Yeoman自定义脚手架](https://gitee.com/dadami/fed-e-task-02-1fed-e-task-02-1/tree/master/code/generators-vue-cli) #### 问题4 Grunt 自动构建 [Grunt 自动构建](https://gitee.com/dadami/fed-e-task-02-1fed-e-task-02-1/tree/master/code/grunt-code) #### 问题5 Gulp 自动构建 [Gulp 自动构建](https://gitee.com/dadami/fed-e-task-02-1fed-e-task-02-1/tree/master/code/gulp-code). ### Promise 实现 #### 第一版 ```javascript /** * 1 Promise 就是一个类, 在执行这个类的时候 需要传递一个执行器进去 执行起就会立即执行 * 2 Promise 中有三种状态 分别为成功 fulfilled 失败 reject 等待 pending * pending -> fulfilled * pending -> reject * 一旦状态确定就不可更改 * 3 resolve 和reject 函数是用来更改状态的 * resolve: fillfilled * reject: rejected * 4 then 方法内部做的事情就判断状态, 如果状态是成功,调用成功的回调函数 * 如果状态是失败, 调用失败回调函数 then 方法是被定义在原型对象中的 * * 5 then成功回调有一个参数 表示成功之后的值 then 失败回调有一个参数 * 表示失败之后的值 */ const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' class MyPromise { // executor执行器 constructor(executor) { // promise 状态 this.status = PENDING; // 成功之后的值 this.value = undefined; // 失败后的原因 this.reason = undefined; // 指向Promise对象 let resolve = value => { // 如果状态不是等待 阻止程序向下执行 if (this.status !== PENDING) return; // 将状态更改为成功 this.status = FULFILLED; // 保存成功之后的值 this.value = value } let reject = reason => { // 如果状态不是等待 阻止程序向下执行 if (this.status !== PENDING) return; this.status = REJECTED // 保存失败后的原因 this.reason = reason } executor(resolve, reject) } then(successCallback, failCallback) { // 判断状态 if (this.status === FULFILLED) { successCallback(this.value) } else if (this.status === REJECTED) { failCallback(this.reason) } } } module.exports = MyPromise; const MyPromise = require('./myPromise.js') let p1 = new MyPromise((resolve, reject) => { resolve('成功') }) p1.then((value) => { console.log(value) }, (err) => { console.log(err) }) // 成功 ``` ### 考虑异步代码 ```javascript // 在constructor 中添加 // 成功后的回调 this.successCallback = undefined; // 失败后的回调 this.failCallback = undefined; // 在resolve 中添加 // 判断成功回调是否存在 如果存在 则调用 this.successCallback && this.successCallback(this.value) // 在reject中添加 // 判断失败回调是否存在 如果存在 则调用 this.failCallback && this.failCallback(this.value) let p1 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('成功') }, 2000) }) p1.then((value) => { console.log(value, '5') }, (err) => { console.log(err) }) ``` ### 实现then方法多次调用添加多个处理函数 - 分同步 异步进行 ```javascript // 将成功回调和失败回调 改为数组, 因为数组才能同时存储多个回调函数 // 成功后的回调 this.successCallback = []; // 失败后的回调 this.failCallback = []; // 判断状态 调用数组push方法 if (this.status === FULFILLED) { successCallback(this.value) } else if (this.status === REJECTED) { failCallback(this.reason) } else { this.successCallback.push(successCallback) // 将成功回调推入 this.failCallback.push(failCallback) // 将失败回调推入 } let p1 = new MyPromise((resolve, reject) => { setTimeout(() => { // resolve('成功') reject('失败') }, 2000) }) p1.then((value) => { console.log(value,) }, (err) => { console.log(err) }) p1.then((value) => { console.log(value,) }, (err) => { console.log(err) }) p1.then((value) => { console.log(value,) }, (err) => { console.log(err) }) // 成功 // 成功 // 成功 ``` ### then 方式链式调用 - Promise 对象下的then方法是可以被链式调用的, 后面then方法回调函数拿到的值,其实就是上一个then方法回调函数的返回值 - 如何把上一个then方法回调函数返回值传给下一个then方法的回调函数 - 如何实现then 方法的链式调用, then方法是Promise对象的下面的方法, 那么每一个then方法它都应该返回一个Promise对象, 这样才能实现链式调用then ```javascript then(successCallback, failCallback) { // 创建一个新的 promise // 改造代码, 传递一个执行器, 立即执行, 原有的代码也需要立即执行 let promise2 = new MyPromise((resolve, reject) => { // 判断状态 if (this.status === FULFILLED) { // 执行成功调用成功回调函数,拿到返回值 // x 是返回值 '大白菜' let x = successCallback(this.value); // 将返回值传给下一个then resolve(x) } else if (this.status === REJECTED) { failCallback(this.reason); } else { this.successCallback.push(successCallback) // 将成功回调推入 this.failCallback.push(failCallback) // 将失败回调推入 } }); return promise2 // 返回 } let p1 = new MyPromise((resolve, reject) => { resolve('成功') }) p1.then((value) => { console.log(value) return '大白菜' }).then((value) => { console.log(value) }) // 成功 // 大白菜 ``` ### then 链式调用优化 在链式调用then方法的时候, 回调函数中可以返回一个普通值,也可以返回一个Promise对象。如果返回的是普通值,可以直接调用resolve, 如果返回的是一个Promise对象, 需要对状态进行判断,如果返回的状态是成功的,需要调用resolve方法 将成功的状态传递给下一个Promise对象, 如果返回的状态是失败的,需要调用reject方法把失败的结果传递给下一个Promise对象 ```javascript then(successCallback, failCallback) { // 创建一个新的 promise // 改造代码, 传递一个执行器, 立即执行, 原有的代码也需要立即执行 let promise2 = new MyPromise((resolve, reject) => { // 判断状态 if (this.status === FULFILLED) { // 执行成功调用成功回调函数,拿到返回值 // x 是返回值 '大白菜' let x = successCallback(this.value); // 判断 x 的值是普通值还是promise对象 // 如果是普通值, 直接调用resolve // 如果是promise 对象, 查看promise对象的返回结果 // 在根据promise对象返回的结果 决定调用resolve 还是reject // 将返回值传给下一个then resolvePromise(x, resolve, reject) } else if (this.status === REJECTED) { failCallback(this.reason); } else { this.successCallback.push(successCallback) // 将成功回调推入 this.failCallback.push(failCallback) // 将失败回调推入 } }); return promise2 // 返回 } function resolvePromise (x, resolve, reject) { if (x instanceof MyPromise) { // promise 对象 // x.then(value => {resolve(value)}, resson => {reject(resson)}) x.then(resolve, reject) } else { // 普通值 resolve(x) } } // 测试 function other() { return new MyPromise((resolve, reject) => { resolve('大白菜other') }) } p1.then((value) => { console.log(value) return other() }).then((value) => { console.log(value) }) // 成功 // 大白菜 ``` ### Promise then方法链式调用识别Promise对象自动返回 - 当链式调用promise 对象下面的then方法的时候, 在then方法回调函数中可以返回promise 对象,但我们需要考虑另外一种情况,在then方法回调函数中不能返回当前这个then方法他所返回的promise对象, 如果返回了then方法返回的promise对象,就会发生循环调用 #### 示例 ```javascript let promise = new Promise((resolve, reject) => { resolve('大白菜') }) let p1 = promise.then((value) => { console.log(value) return p1 }) // 程序报错 // TypeError: Chaining cycle detected for promise # p1.then((value) => { }, (err) => { // 拿到错误原因 console.log(err.message) }) // 在自己Promise 判断这种情况 // 返回的 promise 对象就是promise 2 // 那么成功的回调 返回的promise对象就是 x // 判断 peomise2 与 x 是否相等, // 相等就是自己返回了自己, 需要将状态放到reject // 改成异步代码 then(successCallback, failCallback) { // 创建一个新的 promise // 改造代码, 传递一个执行器, 立即执行, 原有的代码也需要立即执行 let promise2 = new MyPromise((resolve, reject) => { // 判断状态 if (this.status === FULFILLED) { setTimeout(() => { // 执行成功调用成功回调函数,拿到返回值 // x 是返回值 '大白菜' let x = successCallback(this.value); resolvePromise(promise2, x, resolve, reject) }, 0) } else if (this.status === REJECTED) { failCallback(this.reason); } else { this.successCallback.push(successCallback) // 将成功回调推入 this.failCallback.push(failCallback) // 将失败回调推入 } }); return promise2 // 返回 } function resolvePromise (promise2, x, resolve, reject) { // 判断是否相等 if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise #')) } if (x instanceof MyPromise) { // promise 对象 // x.then(value => {resolve(value)}, resson => {reject(resson)}) x.then(resolve, reject) } else { // 普通值 resolve(x) } } // 测试 let promise = new MyPromise((resolve, reject) => { resolve('成功') }) function other() { return new MyPromise((resolve, reject) => { resolve('大白菜') }) } let p1 = promise.then((value) => { console.log(value) return p1 }) p1.then((value) => { console.log(value) }, (err) => { console.log(err) }) // 成功 // Chaining cycle detected for promise # ``` ### Promise 捕获错误及then 链式调用其他状态码 - 执行构造器 - then 回调函数捕获这两个地方的错误 ```javascript try { executor(resolve, reject) } catch (e) { reject(e) } let promise = new MyPromise((resolve, reject) => { throw new Error('executor error') resolve('成功') }) promise.then((value) => { console.log(value) }, (err) => { console.log(err) }) // Error: executor error setTimeout(() => { try { let x = successCallback(this.value); // 判断 x 的值是普通值还是promise对象 // 如果是普通值, 直接调用resolve // 如果是promise 对象, 查看promise对象的返回结果 // 在根据promise对象返回的结果 决定调用resolve 还是reject resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }, 0) // 测试 let promise = new MyPromise((resolve, reject) => { resolve('成功') }) promise.then((value) => { console.log(value) throw new Error('then error') }, (err) => { console.log(err.message) }).then((value) => { console.log(value) }, err => { console.log(大白菜) console.log(err.message) }) // 成功 // 大白菜 // then error ``` #### 修改失败的地方 ```javascript // 改成异步代码 then(successCallback, failCallback) { // 创建一个新的 promise // 改造代码, 传递一个执行器, 立即执行, 原有的代码也需要立即执行 let promise2 = new MyPromise((resolve, reject) => { // 判断状态 if (this.status === FULFILLED) { setTimeout(() => { try { let x = successCallback(this.value); // 判断 x 的值是普通值还是promise对象 // 如果是普通值, 直接调用resolve // 如果是promise 对象, 查看promise对象的返回结果 // 在根据promise对象返回的结果 决定调用resolve 还是reject resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }, 0) } else if (this.status === REJECTED) { setTimeout(() => { try { let x = failCallback(this.reason); // 判断 x 的值是普通值还是promise对象 // 如果是普通值, 直接调用resolve // 如果是promise 对象, 查看promise对象的返回结果 // 在根据promise对象返回的结果 决定调用resolve 还是reject resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }, 0) } else { this.successCallback.push(successCallback) // 将成功回调推入 this.failCallback.push(failCallback) // 将失败回调推入 } }); return promise2 // 返回 } //测试 let promise = new MyPromise((resolve, reject) => { reject('error') }) promise.then((value) => { console.log(value) }, err => { console.log(err) return '大白菜' }).then((value) => { console.log(value) }) // 输出 // error // 大白菜 ``` #### 当代码是异步的时候 将原来代码 ``` this.successCallback.push(successCallback) this.failCallback.push(failCallback) ``` 改造 ```javascript this.successCallback.push(() => { setTimeout(() => { try { let x = successCallback(this.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) // 将成功回调推入 this.failCallback.push(() => { setTimeout(() => { try { let x = failCallback(this.reason); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) // 将失败回调推入 // 测试 let promise = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('success') }, 2000) }) promise.then((value) => { console.log(value) return '大白菜' }, err => { console.log(err) return '大白菜1' }).then((value) => { console.log(value) }) // success // 大白菜 ``` ### then参数变为可选参数 - then 方法有两个参数, 一个成功回调, 一个失败回调 - 这两个参数都是可选参数 ```javascript let promise = new Promise((resolve, reject) => { resolve(100) }) promise .then() .then() .then(value => { cosole.log(value) }) ``` - 在这种情况下, promise 会依次往下传递 - 我们需要在then 方法中判断successCallback是否存在,如果不存在就补一个回调函数 ``` successCallback = successCallback ? successCallback : value => value; failCallback = failCallback ? failCallback : reason => { throw reason }; // 测试 let promise = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('success') }, 2000) }) promise.then().then().then(value => { console.log(value) }) //success ``` #### Promise All 的实现 - Promise All 特点在all 中的所有Promise 对象, 如果他的状态都是成功的,那么 all方法就是成功的,如果有一个是失败的, 那么就是失败的 - 利用类 .上all 所以 all 是一个静态方法 ``` static all(array) { let result = [] let index = 0 return new MyPromise((resolve, reject) => { // 执行for 循环有异步操作,循环没有等待异步操作。 // 如果index 等于 array的length 就调用resolve function addData (key, value) { result[key] = value; index++ if (index === array.length) { resolve(result) } } // 需要判断是普通值, 还是Promise 对象 for (let i = 0; i < array.length; i++) { let current = array[i]; if (current instanceof MyPromise) { // promise 对象 current.then(value => addData(i, value), reason => reject(reason) ) } else { // 普通值 addData(i, array[i]) } } }) } ``` - Promise.all 是解决异步并发问题, 允许按照异步代码调用的顺序得到异步代码执行的结果, 由于all 方法是静态方法, all 前面定义 static 关键字, all 方法接收一个数组作为参数, all方法的返回值也是一个Promise 对象, 在Promise 对象中通过循环 传递的数组,在循环的过程判断是普通值,还是Promise 对象, 进行不同的调用 #### 测试 ```javascript let promise = new MyPromise((resolve, reject) => { setTimeout(() => { reject('大白菜') }, 2000) }) function p1() { return new MyPromise((resolve, reject) => { setTimeout(() => { resolve('p1') }, 1000) }) } function p2() { return new MyPromise((resolve, reject) => { setTimeout(() => { resolve('p1') }, 1000) }) } MyPromise.all(['a', 'b', p1(), p2(), 'c']).then(result => { console.log(result) }) // [ 'a', 'b', 'p1', 'p1', 'c' ] ``` ### Promise.resolve() - 将给定的值转换为Promise 对象, 也就是说Promise.resolve 的返回值就就是一个Promise对象,在返回的Promise 对象中会包裹给定的这个值 - 在resolve 的内部, 会创建一个Promise 对象,并把这个值包裹在Promise对象中,然后把创建出来的Promise 对象最为resolve的返回值,正是因为这样,我们才能后面进行链式调用then方法, 通过then方法的成功回调函数来拿到这个值, Promise.resolve()也可以接收一个Promise 对象, 在Promise.resolve()内部会判断给定的值是否是Promise 对象,如果是,会原封不动把promise 在作为Promise.resolve的返回值,不是就创建一个Promise对象 ```javascript static resolve(value) { if (value instanceof MyPromise) return value; return new MyPromise(resolve => resolve(value)) } // 测试 function p1() { return new MyPromise((resolve, reject) => { setTimeout(() => { resolve('p1') }, 1000) }) } MyPromise.resolve('大白菜').then(value => console.log(value)) MyPromise.resolve(p1()).then(value => console.log(value)) // 大白菜 // p1 ``` ### Promise.finally() - 两个特点 - 无论当前这个Promise对象最终的状态是成功还是失败,finally方法这个会回调函数始终都会执行一次 - 在finally的后面可以链式调用then 方法来拿到当前这个Promise对象最终返回的结果 ```javascript // finally链式调用返回Promise finally(callback) { return this.then(value => { callback(); return value }, reason => { callback(); throw reason }) } // 测试 function p1() { return new MyPromise((resolve, reject) => { resolve('p1 reject') // reject('p1 reject') }) } p1().finally(() => { console.log('finally'); }).then(value => { console.log(value); }, reason => { console.log(reason); }) ``` - 在finally 的回调函数中,其实可以在return 一个Promise 对象 - 后面的then需要等待setTimeout之后执行 - 借助resolve方法 - 如果callback返回的是普通值,转换Promise对象, 等待Promise 对象执行完成,如果返回的是Promise 对象,还等待你执行完成,在返回value - 优化上面的代码 ```javascript finally(callback) { return this.then(value => { return MyPromise.resolve(callback()).then(() => value); }, reason => { return MyPromise.resolve(callback()).then(() => { throw reason },); }) } // 测试 function p1() { return new MyPromise((resolve, reject) => { setTimeout(() => { resolve('p1 resolve') }, 2000) }) } function p2() { return new MyPromise((resolve, reject) => { resolve('p2 resolve') // reject('p2 reject') }) } p2().finally(() => { console.log('finally'); return p1() }).then(value => { console.log(value); }, reason => { console.log(reason); }) // finally // 等待2s后执行 输出p2 resolve ``` ### Promise.catch() - catch方法的作用是用来处理当前这个Promise 对象最终状态为失败的情况的 -就是说当我们调用then方法时候,我们可以不传递失败回调, 如果不传失败回调,那么失败回调就可以被catch捕获,从而去执行传入到catch方法的回调函数 - 只需要在catch方法内部去调用then方法就可以了 ``` ```