Promise的使用方法和自定义promisify

198 阅读5分钟

1. Promise的使用

如果Promise 函数执行时抛出错误,则会改变状态为 reject ,继而调用 reject 回调函数

let promise = new Promise((resolve, reject) => {
     resolve(a);  // 此时 a 未被声明,未被定义
})

promise.then(
  (value) => {
    console.log("resolve:" + value);
  },
  (reason) => {
    console.log("reject:" + reason);  // 此时错误被捕获
  }
);

image.png

1.1 Promise.prototype.catch()

【 作用 】于指定发生错误时的回调函数,返回一个Promise对象

【 本质 】 .catch().then(null, reject) .then(undefined, reject) 的另一种写法

promise.then(null, (reason) => { //不写回调函数的时候,填null
  console.log('reject: ', reason)
});
// 等同于
promise.catch(function(reason) {
  console.log('reject: ', reason)
});

捕获异常时推荐的写法

promise.then(() => {

}).catch(() => {

})

【 注意 】.then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获

1.2 固化

【 特点 】

  • Promise 状态固化后不会改变,状态固化后,后续代码的错误不会被 catch捕获;但是代码会被执行,也就是说resolvereject不会终止整个函数执行
let promise = new Promise((resolve, reject) => {
  // 状态固化
  resolve("ok");
  // reject("no"); 
  console.log(a);  // 不论是 resolve 还是 reject ,后续的代码都会被执行,但是错误不会被catch捕获
});

promise
  .then((value) => {
    console.log(value);
  })
  .catch((error) => {
    console.log(error);
  });

【 注意 】

  1. 类似于冒泡的特性,异常会被一层一层的冒泡直到 catch 捕获
  2. then() 什么参数都不传,会被忽略掉
let promise = new Promise((resolve, reject) => {
  console.log(a)
})

promise
  .then((value) => {
    console.log(value);
    return "hello";
  })
  .then()
  .then() // .then什么参数都不传,会被忽略掉,相当于没有添加这个.then
  .then((value) => {
    console.log("then: ", value);
  })
  .catch((error) => {
    // 异常会被一层一层的冒泡直到 catch 捕获
    console.log(error);
  });

1.3 状态的依赖

状态失效

  • 当一个 Promise的状态被另一个 Promise状态引用时,被引用的Promise状态决定了第一个Promise的状态,在此之前该 Promise状态都是无效的
const p1 = new Promise((resolve, reject) => {
    setTimeout(function() {
        reject(new Error('fail'));
        // resolve('ok')		// 打印 ok
    }, 3000)
})
// p1 作为参数传递给 p2 的时候,此时 p2 的状态无效,取决于当前 p1 的状态,只有 p1 拿到结果之后才会执行 p2
const p2 = new Promise((resolve, reject) => {
    setTimeout(function() {
        resolve(p1);
    }, 1000)
})
p2.then((result) => console.log(result))
    .catch((error) => console.log(error))

2. 回调的管理

Tip constructor上的方法

2.1 Pomise.all()

管理多个Promise实例,参数是具备iterator接口,一般来说是数组

【 用法 】

  • 所有都成功,返回一个按promise顺序执行的结果的数组
  • 如果有失败,进行了reject 就会被catch捕获,返回第一个失败对象的错误信息,没有reject则会返回数组,对应失败的那个数组的值为undefined
const fs = require('fs');

// promise 包装异步操作
let promise1 = new Promise((resolve, reject) => {
    fs.readFile('./name.txt', 'utf-8', (err, data) => {
        if (err) {
            reject(err);
        }
        resolve(data);
    });
})
let promise2 = new Promise((resolve, reject) => {
    fs.readFile('./number.txt', 'utf-8', (err, data) => {
        if (err) {
            reject(err);
        }
        resolve(data);
    });
})
let promise3 = new Promise((resolve, reject) => {
    fs.readFile('./score.txt', 'utf-8', (err, data) => {
        if (err) {
            reject(err);
        }
        resolve(data);
    });
})

const p = Promise.all([promise1, promise2, promise3]);

p.then(res => console.log(res))
    .catch(err => console.log(err));

【 证明 】 当有失败时被catch捕获,返回第一个失败对象的错误信息

let promise1 = new Promise((resolve, reject) => {
    setTimeout(function() {
        reject('promise1: 1000ms');
        // resolve('promise1: 1000ms');
    }, 1000)
})
let promise2 = new Promise((resolve, reject) => {
    setTimeout(function() {
        reject('promise2: 2000ms');
        // resolve('promise2: 2000ms');
    }, 2000)
})
let promise3 = new Promise((resolve, reject) => {
    setTimeout(function() {
        reject('promise3: 3000ms');
        // resolve('promise3: 3000ms');
    }, 3000)
})
// 如果都成功,拿到所有成功的值组成一个数组返回
// 如果有失败并被 .catch,返回第一个触发失败的promise
let p = Promise.all([promise1, promise2, promise3]);
p.then(res => console.log(res))
    .catch(err => console.log(err))

image.png

1.2 Promise.race()

【 用法 】 不论成功还是失败,都会返回一个promise对象,返回最先执行完成的结果

let promise1 = new Promise((resolve, reject) => {
    setTimeout(function() {
        // reject('promise1: 1000ms');
        resolve('promise1: 1000ms');
    }, 4000)
})
let promise2 = new Promise((resolve, reject) => {
    setTimeout(function() {
        // reject('promise2: 2000ms');
        resolve('promise2: 2000ms');
    }, 2000)
})
let promise3 = new Promise((resolve, reject) => {
    setTimeout(function() {
        // reject('promise3: 3000ms');
        resolve('promise3: 3000ms');
    }, 3000)
})
// 返回第一个执行完成的结果
let p = Promise.race([promise1, promise2, promise3]);
p.then(res => console.log(res))
    .catch(err => console.log(err))

image.png

3. Promise.resolve()

Tip: constructor上的方法

【 用法 】 可以把一个 thenable对象转为Promise 对象

  • 如果有 then 方法,可以直接通过Promise.resolve部署thenable,此时promise的状态是根据then中的状态决定的

3.1 部署 thenable

Tip: 具有 then 属性的对象都可以叫 thenable

let thenable = {
  then(resolve, reject) {
    resolve(2);
    // reject(42)
  },
};

// 通过 Promise.resolve(),把一个对象转成 Promise 对象
let promise = Promise.resolve(thenable);		

promise
  .then((value) => {
    console.log(value);
  })
  .catch((error) => {
    console.log(error);
  });

4.回调地狱的优化

let fs = require('fs');

fs.readFile('./name.txt', 'utf-8', (err, data) => {
    if (err) {
        console.log(err)
    }
    if (data) {
        fs.readFile(data, 'utf-8', (err, data) => {
            console.log(data);

            fs.readFile(data, 'utf-8', (err, data) => {
                console.log(data);
            })
        })
    }
    console.log(data);
});

优化后:

function readFile(path) {
  let fs = require("fs");
  return new Promise((resolve, reject) => {
    fs.readFile(path, "utf-8", (error, data) => {
      if (error) {
        console.log(`${error}`);
      }
      resolve(data);
    });
  });

  readFile("./name.txt")
    .then((value) => {
      readFile(value);
    })
    .then((value) => {
      readFile(value);
    })
    .then((value) => {
      readFile(value);
    })
    .catch((error) => {
      console.log(error);
    });
}

5. 自定义promisify

5.1 实现promisify

【 用法 】函数 Promise

const fs = require('fs');
const util = require('util');

function promisify(fn) {
  return function (...args) {
    return new Promise((resolve, reject) => {
      // 执行原本的函数  执行回调函数
      fn(...args, (error, data) => {
        if (error) {
          return reject(error);
        }
        resolve(data);
      })
    })
  }
}

// 传入一个函数作为参数
let readFile = promisify(fs.readFile);
// node 中提供了 promisify 方法
let readFile = util.promisify(fs.readFile);

readFile('./name.txt', 'utf-8')
    .then(data => readFile(data, 'utf-8'))
    .then(data => readFile(data, 'utf-8'))
    .then(data => console.log(data))

实现promisifyAll

【 用法 】 将所有fs模块上的方法都变成Promise化的方法

const fs = require('fs');

function promisify(func) {
  return function(...arg) {
    return new Promise((resolve, reject) => {
      // 执行原本的函数  执行回调函数
      func(...arg, (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      })
    })
  }
}

// fs.writeFile 变成 fs.writeFileAsync
// fs.readFile  变成 fs.readFileAsync 		以此类推
function promisifyAll(obj) {
  for (let [key, fn] of Object.entries(obj)) {
    if (typeof fn === 'function') {
      obj[key + 'Async'] = promisify(fn);
    }
  }
  return obj;
}

// 或者
function promisifyAll(fns) {
  Object.keys(fns).map((fnName) => {
    if (typeof fns[fnName] === 'function') {
      fns[fnName + 'Async'] = this.promisify(fns[fnName]);
    }
  })
  return fns;
}

promisifyAll(fs);
fs.readFileAsync('./name.txt', 'utf-8')
  .then(data => fs.readFileAsync(data, 'utf-8'))
  .then(data => fs.readFileAsync(data, 'utf-8'))
    .then(data => console.log(data))