一、promise深入
-
若在executor里面抛出错误,promise状态会直接更改为reject
let promise = new Promise(function(resolve,reject){ resolve(a); // 还没来得及执行就抛出了错误,将状态更改为reject,然后触发回调 }) promise.then((val)=>{ console.log('resolve: '+val) },(reason)=>{ console.log('reject: '+reason) }) // reject: a is not defined
-
then的简写
至少有一个参数,不指定用null占位
promise.then(null,(reason)=>{ console.log('reject: '+reason) }); //等同于 promise.catch(function(reason){ console.log('reject: '+reason) })
-
catch
-
基本使用
也可以捕获异常
let promise = new Promise(function(resolve,reject){ resolve(a); }) promise.catch(function(reason){ console.log(reason) }); // ReferenceError: a is not defined
-
语义化写法
当需要捕获异常的时候,推荐使用第二种写法
promise.then(function(){ },function(){ }); // 推荐 promise .then(function(){ }) catch(function(){ })
-
特性
- 冒泡的特性
- 状态固化之后,无法捕获异常
- 链式调用 then() 的时候,如果什么参数都不传会被直接忽略
let promise = new Promise(function(resolve,reject){ resolve(a); }) promise.then(function(val){ console.log(val); }).then().then().catch(function(err){ console.log(err); }) // ReferenceError: a is not defined
-
-
promise固化
如果promise状态发生变化,就意味着状态不会再发生改变
let promise = new Promise(function(resolve,reject){ resolve('ok'); console.log(a); // 这里也执行了,但由于状态已经固化了,所以catch捕获不到 }) promise.then((val)=>{ console.log(val); }).catch((err)=>{ console.log(err); }); //ok
-
状态依赖
每一个promise代表的是一个异步操作,每个异步操作都有相应的状态,那么在有多个promise嵌套的情况下,状态又是怎样传递的呢?
当executor的resolve、reject的参数是另一个promise的时候,那么当前promise的状态,就取决于另一个promise的状态
let p1 = new Promise((resolve,reject)=>{ //也是同步执行 setTimeout(function(){ reject(new Error('fail')); },3000); }); let p2 = new Promise((resolve,reject)=>{ setTimeout(function(){ resolve(p1); },1000); }); p2.then(result=>console.log(result)) .catch(err=>console.log(err)); //Error: fail
当前p2的状态依赖于p1,就会导致p2自己的状态无效,它的状态取决于p1的状态
-
resolve/reject不会终止executor执行
const p1 = new Promise((resolve,reject)=>{ //resolve(1); reject(new Error()); //微任务 console.log(2); }); p1.then(res=>console.log(res)) .catch(err=>console.log(err)); console.log(3); //2 3 Error
-
race/all管理异步的关系
promise通过race、all管理异步之间的关系
-
all
批量处理多个异步操作,返回值是promise对象
参数是具有iterator接口的对象(数组),里面是相应的promise
只有所有promise状态为成功情况下,才会触发成功回调
成功回调参数是一个数组,其顺序与iterable里promise顺序一致
const fs = require('fs'); let promise1 = new Promise((resolve,reject)=>{ fs.readFile('./name.txt','utf-8',function(err,data){ if(err){ console.log(err); } resolve(data); }); }); let promise2 = new Promise((resolve,reject)=>{ fs.readFile('./number.txt','utf-8',function(err,data){ if(err){ console.log(err); } resolve(data); }); }); let promise3 = new Promise((resolve,reject)=>{ fs.readFile('./score.txt','utf-8',function(err,data){ if(err){ console.log(err); } resolve(data); }); }); const p = Promise.all([promise1,promise2,promise3]); //返回值是promise对象 p.then(res=>console.log(res)); //[ './number.txt', 'score.txt', '90' ]
只要有一个promise状态为失败,就会触发失败回调
如果全部promise状态都为失败,则只触发iterable里第一个promise状态为失败的回调,其余直接忽略
let promise1 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('promise1: 1000ms') },1000) }) let promise2 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('promise2: 2000ms') },2000) }) let promise3 = new Promise((resolve,reject)=>{ setTimeout(function(){ reject('promise3: 3000ms') },3000) }) let p = Promise.all([promise1,promise2,promise3]); p.then(res=>console.log(res) ).catch((error)=>{ console.log(error); }) //失败回调:promise1:1000ms 后面直接忽略
-
race
批量处理多个异步操作,返回值是promise对象
任意一个promise状态为成功或失败,就会触发相应的回调,其余直接忽略
let promise1 = new Promise((resolve,reject)=>{ setTimeout(function(){ //resolve('promise1: 1000ms'); reject('promise1: 1000ms'); },1000) }) let promise2 = new Promise((resolve,reject)=>{ setTimeout(function(){ //resolve('promise2: 2000ms'); reject('promise2: 2000ms'); },2000) }) let promise3 = new Promise((resolve,reject)=>{ setTimeout(function(){ //resolve('promise3: 3000ms'); reject('promise3: 3000ms'); },3000) }); let p = Promise.race([promise1,promise2,promise3]); p.then(res=>console.log(res) ).catch((error)=>{ console.log(error); }); //promise1:1000ms 不管成功或失败都返回这个,因为它最先完成 其余直接忽略
-
-
构造器上的resolve/reject
-
thenable
绑定了then方法的对象就是thenable,用于转换成promise对象
-
resolve —— 转换成功promise对象
返回一个promise对象,当前promise的状态取决于resolve的参数
-
是一个thenable
let obj = { then:function(resolve,reject){ resolve(42); } } let p1 = Promise.resolve(obj); p1.then(function(value){ console.log(value); }); //42
-
字符串
let p1 = Promise.resolve('hello'); p1.then(res=>{ console.log(res) }); //hello
-
没有参数
let p1 = Promise.resolve(); p1.then(res=>{ console.log(res) }); //undefined
-
-
reject —— 转换失败promise对象
返回一个promise对象,当前promise的状态取决于resolve的参数
-
thenable(不要用)
let obj = { then: function(resolve, reject) { reject(new Error('hello')); } } let p1 = Promise.reject(obj); p1.then(null, function(value) { console.log(value); }); //{ then: [Function: then] } what the hell? 所以不要用
-
字符串
let p1 = Promise.reject('hello'); p1.then(null,res=>{ console.log(res) }); //hello
-
不传参
let p1 = Promise.reject(); p1.then(null,res=>{ console.log(res) }); //undefined
-
-
-
函数Promise化
将异步函数用promise包装一遍
const fs = require('fs'); function readFile(path){ return new Promise((resolve,reject)=>{ fs.readFile(path,'utf-8',(err,data)=>{ if(data){ resolve(data); } }) }) } readFile('./name.txt') .then(data => readFile(data)) .then(data => readFile(data)) .then(data => console.log(data)); //99
二、promisify
-
promisify概念
能够将所有函数promise化的方法
-
promisify实现(node)
const fs = require('fs'); function promisify(func){ return function(...arg){ // 1.返回promise化的方法,传入需promise化的方法 return new Promise((resolve,reject)=>{ // 2.调用promise化的方法,需传入相应参数 func(...arg,(err,data)=>{ // 3.根据参入的方法和参数,执行相应的方法 if(err){ reject(err); }else{ resolve(data) } }) }) } } let readFile = promisify(fs.readFile); // 第一步:传入需promise化的方法 readFile('./name.txt','utf-8') // 第二步:需传入相应参数 .then(data=>readFile(data,'utf-8')) .then(data=>readFile(data,'utf-8')) .then(data=>console.log(data)); //99
-
node工具函数中也提供了promisify
const util = require('util'); 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)); //99
-
优化node提供的promisify
把fs上的所有方法转成promisify方法
const fs = require('fs'); const util = require('util'); let readFile = util.promisify(fs.readFile); let readdir = util.promisify(fs.readdir); let writeFile = util.promisify(fs.writeFile); //每次使用前都要传入promise化的方法,比较麻烦,本次优化就是解决这个问题 function promisify(func){ return function(...arg){ return new Promise((resolve,reject)=>{ func(...arg,(err,data)=>{ if(err){ reject(err); }else{ resolve(data) } }) }) } } function promisifyAll(obj){ for(let [key,fn] of Object.entries(obj)){ // 注意:这里for...of本不能遍历obj,但这里用Object.entries把obj转成了键和值的数组,所以可以迭代 if(typeof fn === 'function' ){ obj[key+'Async'] = promisify(fn); } } } promisifyAll(fs) fs.readFileAsync('./name.txt','utf-8') .then(data=>readFileAsync(data,'utf-8')) .then(data=>readFileAsync(data,'utf-8')) .then(data=>console.log(data)); //99