Node-JS专精09_01手写Promise

Promise

答题方法论

顺序

  • 该技术解决什么问题 why
  • 该技术是怎么解决它的 how
  • 该技术有什么优点 pros
  • 该技术有什么缺点 cons
  • 如何解决这些缺点 more

promise 为例

回调地狱真的是个问题吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fs.readdir(source, function (err, files) {
if (err) {
console.log('Error finding files: ' + err) }
else {
files.forEach(function (filename, fileIndex){
console.log(filename)
gm(source + filename).size(function (err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height)
.write(dest + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
})
  • 有没有可能是这个程序员水平不行
    • 是不是代码写错了,不一定非要嵌套吧?

改写上面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fs.readdir(source, (err, files)=> {
travalFiles = () => {
if(err){ return console.log('Error: 找不到目录 ' + err) }
files.forEach(gmFile)
}
gmFile = (filename) => {
console.log(filename)
gm(source + filename).size(afterGetSize)
}
afterGetSize = (err, values) => {
if (err) return console.log('无法读取文件尺寸: ' + err)
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach((width, widthIndex) => resize(width, aspect))
}
resize = (width, aspect) => {
height = Math.round(width / aspect)
console.log('将' + filename + '的尺寸变为 ' + width + 'x' + height)
this.resize(width, height)
.write(dest + 'w' + width + '_' + filename, (err) =>
err && console.log('Error writing file: ' + err)
)
}
travalFiles(err, files)
})
  • 但是现状就是:如果你用回调大家就会写出低频的这种 回调地狱的代码

001所以如果面试官问题Promise解决了什么问题?

你还是要回答回调地狱

002那么就会继续问Promise是怎么解决这个回调地狱的问题

003 Promise的优势

  • 减少缩进(异步的代码变成同步的书写方式)
    • 把函数里的函数变成 then下面的then
  • 消除if(err)
    • 错误处理单独放在一个函数里
    • 如果不处理,就一直向后抛
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
f1(xxx,function f2(a){
f3(yyy,function f4(b){
f5(a+b,function f6(){})
})
})


// 变成这样
f1(xxx)
.then(f2) // f2内部调用 f3
.then(f4) // f4内部调用 f5
.then(f6)
提问: f5怎么得到 a和b
答案: f2的输出作为 f4的输入

// 消除if(err)
f1(xxx)
.then(f2,error1) // f2内部调用 f3
.then(f4,error2) // f4内部调用 f5
.then(f6,error3)
.then(null,errorAll)
// 最后一个可以写出 .catch

004用户怎么用Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 之前
function 摇色子(fn){
setTimeout(()=>{
const result = Math.floor(Math.random()*6+1)
fn(result) // 等价于 fn.call(null, result)
},3000)
}
摇色子(n=>console.log(`摇到了${n}`))

// 现在
function 摇色子(){
// new Promise 接受一个函数,返回一个 Promise 实例
return new Promise((resolve, reject)=>{
setTimeout(()=>{
const result = Math.floor(Math.random()*6+1)
resolve(result)
},3000)
})
}
摇色子().then(n=>console.log(`摇到了${n}`))

Promise的完整API是什么

Promise是一个类

  • JS里类实际就是个函数
  • 类属性 length (可忽略) 因为我们知道它只接受一个参数
  • 类方法:all / allSettled / race / reject / resolve
  • 对象属性:then(重要) finally / catch
  • 对象内部属性: state = pending / fulfilled / rejected

如何实现一个Promise

Promise/A+ 规范文档

开始写代码

  • 按照文档写测试用例
  • 先让用例失败
  • 然后让用例通过
  • 直到把文档的所有情况都考虑清楚

使用chai

更厉害的测试方案

面试技巧

  • 弱势面试:漏出破绽让面试官问,然后立刻给出解决方案
  • 强势面试:什么破绽都不留,直接把标准答案搞出来