整理一些Promise
库bluebird
的常用api
Promis.promisify
promisify
可以帮我们吧一些常用异步方法或者库转化成Promise
对象,这样我们就不用自己封装了;
const fs = require('fs');
const Promise = require('bluebird');
const readFile = Promise.promisify(fs.readFile);
readFile('./data/a.txt').then(data => console.log(data.toString()));
这里仅做个最简单的介绍,更多详细用法查看官网Promis.promisify
Promise.promisifyAll
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync("./data/a.txt").then(contents => { // 实际是readFile方法,Async只是Promise.promisifyAll为我们自动添加的后缀,下面会介绍
console.log(contents);
}).catch(err => {
console.error(err);
});
可以为包装后的函数添加后缀,以区分原始方法,默认的后缀是Async
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'), {suffix: 'Promise'});
fs.readFilePromise("./data/a.txt", "utf8").then(contents => {
console.log(contents);
}).catch(err => {
console.error(err);
});
更多用法查看官网Promis.promisify
Promise.promisifyAll
可以接受数组,一次性promisify
多个类;
var Pool = require("mysql/lib/Pool");
var Connection = require("mysql/lib/Connection");
Promise.promisifyAll([Pool, Connection]);
Cancellation
javascript中的Promise是不支持取消异步操作的,但是bluebird支持取消异步操作,这个是一个很好的特性,能够帮我们解决很多问题,比如在React中,组件挂载时请求接口,如果在接口返回之前这个组件被卸载了,React会抛出一个异常,我们就可以利用bluebird的这一特性,来帮我们处理这个问题;
想要使用Cancellation
必须配置开启,如下第二行代码,此时Promise就可以取消了,还可以定义取消的回调函数,当取消的时候,执行一些操作。
注意:.cancel
操作是同步的
const Promise = require('bluebird');
Promise.config({cancellation: true});
let p1 = new Promise(resolve => {
setTimeout(() => resolve('p1'), 3000);
})
// 第三个参数只有当cancellation配置为true时才有
let p2 = new Promise((resolve, reject, onCancel) => {
setTimeout(() => resolve('p2'), 5000);
onCancel(() => console.log('p2 canceled'))
})
let p3 = new Promise(resolve => {
setTimeout(() => resolve('p3'), 7000);
})
p1.then(res => console.log(res));
p2.then(res => console.log(res));
p3.then(res => console.log(res));
console.log('-----------------');
p2.cancel();
输出
----------------- // 执行栈直接输出
p2 canceled // 调用p2的cancel方法时输出,.cancel方法是同步的,因此会在-----后直接输出
p1 // 等待3秒后输出
p3 // 等待7秒后输出,注意这里不是p1三秒结束后再等待7秒,而是从一开始等待7秒,因为Promise一旦创建就立即执行了
使用已经取消了的Promise
是会报错的SubError [CancellationError]: late cancellation observer
const Promise = require('bluebird');
Promise.config({cancellation: true});
let p2 = new Promise((resolve, reject, onCancel) => {
setTimeout(() => resolve('p2'), 5000);
onCancel(() => console.log('p2 canceled'));
})
p2.cancel(); // 已取消
p2.then(res => {
console.log(res);
}).catch(err => console.log(err));
结果
p2 canceled
SubError [CancellationError]: late cancellation observer
at Promise._then (C:\Users\lyucan\Desktop\pro\newRepo\Repositories\node_modules\bluebird\js\release\promise.js:285:21)
at Promise.then (C:\Users\lyucan\Desktop\pro\newRepo\Repositories\node_modules\bluebird\js\release\promise.js:154:17)
at Object.<anonymous> (C:\Users\lyucan\Desktop\pro\newRepo\Repositories\myrepo
...
Promise.spread
Promise.spread
可以把一个Promise
数组进行解构,例如我们使用Promise.all
进行多个异步操作,正常的.then
拿到的入参就是一个数组,如果我们使用Promise.spread
,那么我们获取到的就是数组里对应的值,而不是一个数组,结果和Promise
对象的数组顺序一一对应,如果只传一个入参,则只获取第一个结果。
const fs = require('fs');
const Promise = require('bluebird');
const readFile = Promise.promisify(fs.readFile);
Promise.all([
readFile('./data/a.txt'),
readFile('./data/b.txt')
]).then(res => console.log(res.map(str => str.toString())))
// [ 'aaaaaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbb' ]
使用Promise.spread
,我们可以看到入参是数组每一项的值,而不是一整个数组
const fs = require('fs');
const Promise = require('bluebird');
const readFile = Promise.promisify(fs.readFile);
Promise.all([
readFile('./data/a.txt'),
readFile('./data/b.txt')
]).spread((res1, res2) => console.log(res1.toString(), res2.toString()))
// 'aaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbb'
Promise.bind
Promise.bind
可以修改当前的this
指向
const fs = require('fs');
const Promise = require('bluebird');
const readFile = Promise.promisify(fs.readFile);
class MyClass {
constructor(){
this.fileName = './data/a.txt';
}
error(error) {
console.log(error)
}
}
MyClass.prototype.method = function() {
console.log(this); // 这里this指向MyClass
return readFile(this.fileName).bind(this) // 绑定this指向
.then((contents) => {
console.log(contents)
console.log(this) // 这里this指向MyClass,如果没有上面的bind(this),那么这里将指向全局对象
}).catch((e) => {
this.error(e.stack); // 这里this指向MyClass,,如果没有上面的bind(this),那么这里将指向全局对象
});
};
let me = new MyClass()
me.method()
Promise.props
Promise.props
和Promise.all
的作用类似,都是组合Promise
对象,最大的区别是,Promise.all
是需要一个具有iterator
接口的对象,例如数组,而Promise.props
的入参是一个key: [Promise]
对象,而结果也会是一个key: [result]
对象,
const fs = require('fs');
const Promise = require('bluebird');
const readFile = Promise.promisify(fs.readFile);
Promise.props({
txtA: readFile('./data/a.txt'),
txtB: readFile('./data/b.txt'),
}).then(result => {
console.log(result.txtA.toString());
console.log(result.txtB.toString());
})
// aaaaaaaaaaaaaaaaaaaaaa
// bbbbbbbbbbbbbbbbbbbbbb
Promise.some
Promise.some
接收两个参数,第一个是Promise
对象数组(不一定是数组,具有iterator
接口就行),第二个参数是返回的个数,这个个数是在所有的Promise
对象中,状态最快变为fulfilled
的前几个,例如下面的代码,读取4个文件,返回最快读到的前2个;
const fs = require('fs');
const Promise = require('bluebird');
const readFile = Promise.promisify(fs.readFile);
Promise.some([
readFile('./data/a.txt'),
readFile('./data/b.txt'),
readFile('./data/c.txt'),
readFile('./data/d.txt'),
], 2).then(res => {console.log(res)})
如果成功的个数小于第二个参数指定的个数,会抛出一个异常
Unhandled rejection AggregateError: aggregate error
at SomePromiseArray._checkOutcome (C:\Users\lyucan\Desktop\pro\newRepo\Repositories\node_modules\bluebird\js\release\some.js:82:17)
at SomePromiseArray._promiseRejected (C:\Users\lyucan\Desktop\pro\newRepo\Repositories\node_modules\bluebird\js\release\some.js:69:17)
at Promise._settlePromise (C:\Users\lyucan\Desktop\pro\newRepo\Repositories\node_modules\bluebird\js\release\promise.js:611:26)
我们可以直接.catch
捕获,但是此时捕获的错误是一个rejected Promise
的数组,具有length
属性和一些数组的操作方法
const fs = require('fs');
const Promise = require('bluebird');
const readFile = Promise.promisify(fs.readFile);
Promise.some([
readFile('./data/a.txt'),
readFile('./data/bd.txt'),
readFile('./data/cd.txt'),
], 2).then(res => {console.log(res)}).catch(err => console.log(err))
此时bd.txt
和cd.txt
文件是不存在的,只有a.txt
能读取成功,而我们定义了成功的数量是前两个,此时就会进入异常
SubError [AggregateError]: aggregate error
at SomePromiseArray._checkOutcome (C:\Users\lyucan\Desktop\pro\newRepo\Repositories\node_modules\bluebird\js\release\some.js:82:17)
...
at processImmediate (internal/timers.js:439:21) {
'0': [OperationalError: ENOENT: no such file or directory, open 'C:\Users\lyucan\Desktop\pro\newRepo\Repositories\myrepo\nodejs\promise\data\bd.txt'] {
cause: [Error: ENOENT: no such file or directory, open 'C:\Users\lyucan\Desktop\pro\newRepo\Repositories\myrepo\nodejs\promise\data\bd.txt'] {
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\Users\\lyucan\\Desktop\\pro\\newRepo\\Repositories\\myrepo\\nodejs\\promise\\data\\bd.txt'
},
isOperational: true,
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\Users\\lyucan\\Desktop\\pro\\newRepo\\Repositories\\myrepo\\nodejs\\promise\\data\\bd.txt'
},
'1': [OperationalError: ENOENT: no such file or directory, open 'C:\Users\lyucan\Desktop\pro\newRepo\Repositories\myrepo\nodejs\promise\data\cd.txt'] {
cause: [Error: ENOENT: no such file or directory, open 'C:\Users\lyucan\Desktop\pro\newRepo\Repositories\myrepo\nodejs\promise\data\cd.txt'] {
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\Users\\lyucan\\Desktop\\pro\\newRepo\\Repositories\\myrepo\\nodejs\\promise\\data\\cd.txt'
},
isOperational: true,
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\Users\\lyucan\\Desktop\\pro\\newRepo\\Repositories\\myrepo\\nodejs\\promise\\data\\cd.txt'
},
length: 2
}
返回的整个错误是可以调用数组方法的,例如
const fs = require('fs');
const Promise = require('bluebird');
const readFile = Promise.promisify(fs.readFile);
Promise.some([
readFile('./data/a.txt'),
readFile('./data/bd.txt'),
readFile('./data/cd.txt'),
], 2).then(res => {console.log(res)}).catch(err => console.log(err.map(errItem => errItem)))
结果
[
[OperationalError: ENOENT: no such file or directory, open 'C:\Users\lyucan\Desktop\pro\newRepo\Repositories\myrepo\nodejs\promise\data\bd.txt'] {
cause: [Error: ENOENT: no such file or directory, open 'C:\Users\lyucan\Desktop\pro\newRepo\Repositories\myrepo\nodejs\promise\data\bd.txt'] {
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\Users\\lyucan\\Desktop\\pro\\newRepo\\Repositories\\myrepo\\nodejs\\promise\\data\\bd.txt'
},
isOperational: true,
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\Users\\lyucan\\Desktop\\pro\\newRepo\\Repositories\\myrepo\\nodejs\\promise\\data\\bd.txt'
},
[OperationalError: ENOENT: no such file or directory, open 'C:\Users\lyucan\Desktop\pro\newRepo\Repositories\myrepo\nodejs\promise\data\cd.txt'] {
cause: [Error: ENOENT: no such file or directory, open 'C:\Users\lyucan\Desktop\pro\newRepo\Repositories\myrepo\nodejs\promise\data\cd.txt'] {
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\Users\\lyucan\\Desktop\\pro\\newRepo\\Repositories\\myrepo\\nodejs\\promise\\data\\cd.txt'
},
isOperational: true,
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\Users\\lyucan\\Desktop\\pro\\newRepo\\Repositories\\myrepo\\nodejs\\promise\\data\\cd.txt'
}
]
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com