经过几天源码研究学习之后,基本上对Promise有了深入的了解,也手动封装了自己了Promise工具类,下面就是我们去在应用场景中去验证这个工具类的使用了
上 - 理论知识
- Promise 规范
- ES6 Promise API
- Polyfill和扩展类库
- Promise 在应用中的错误用法和误区
- 当作回调使用
- 没有返回值
- 没有catch
- catch()与then(null, onRejected)
- 断链
- 穿透
- 长度未知的串行与并行
- Promise.resolve的使用
- 最佳实践
中 - 手动封装
- 实现一个简单的 Promise工具类
- Promise类的结构
- 构造器的初始化
- then方法
- catch方法
- 添加扩展功能函数
- all
- race
- resolve
- reject
- wait
- stop
- always
- done
- defer
- timeout
- sequence
- 测试
- 源码
下 - 实践应用
- 结合应用场景使用Promise
- 使用Promise编写Web Notifications提示
- 使用Deferred封装异步请求
- 异步请求的超时处理
- 基于Promise的fs方法链
Web Notifications提示
显示桌面通知的流程如下:
- 用户进入页面要判断是否默认允许显示桌面通知
- 如果不允许,提示是否允许
- 如果点击了允许,则显示通知
- 如果点击了拒绝,则不显示,后面的任务不执行
- 显示通知失败,则打印显示失败的信息
- 显示成功,判断用户是否点击了通知或点击了关闭
- 如果点击了,则关闭通知
- 如果没有点击,则2s之后自动关闭
- 关闭之后打印消息
代码逻辑
const msgOpt = {
body:
‘你今天还有需要完成的任务哦!’,
icon:
‘http://7xi480.com1.z0.glb.clouddn.com/avatar100.jpg’
}
function requestNoti(){
return
new MPromise(
(resolve, reject)=>{
if(Notification.permission ===
‘granted’){
resolve()
}
else{
Notification.requestPermission(
function (status) {
if (Notification.permission !== status) {
Notification.permission = status;
}
if (status ===
‘granted’) {
resolve()
}
else {
reject(
new
Error(
‘user denied’));
}
});
}
})
}
function showNoti(msgObj){
return
new MPromise(
(resolve, reject)=>{
var n =
new Notification(
‘通知’,msgObj)
MPromise.timeout(closeNoti(),
3000).always(
()=>{
n.close();
resolve();
})
n.addEventListener(
‘error’, reject)
function closeNoti(){
return
new MPromise(
rs => {
n.addEventListener(
‘click’, rs)
n.addEventListener(
‘close’, rs)
})
}
})
}
requestNoti()
.then(
()=>{
console.log(
‘显示 桌面通知’)
return showNoti(msgOpt)
})
.catch(
()=>{
console.log(
‘中断 不允许显示桌面通知’)
return MPromise.stop();
})
.then(
()=>{
console.log(
‘关闭 桌面通知’)
})
.catch(
err=>{
console.log(
‘失败 桌面通知打开失败’)
})
|
这里API的应用
timeout()
用于限制2s钟之内,通知必须关闭
always()
与timeout结合使用
stop()
在promise链的中途停止后面的执行
使用Deferred封装异步请求
这里为什么使用Deferred来封装异步请求呢?
因为使用 new Promise()
的形式会有多一层嵌套,使用 deferred
可以对流程控制自由定制
function ajaxGet(URL) {
var deferred = MPromise.deferred();
var req =
new XMLHttpRequest();
req.open(
'GET', URL,
true);
req.onload =
function () {
if (req.status ===
200) {
deferred.resolve(req.responseText);
}
else {
deferred.reject(
new
Error(req.statusText));
}
};
req.onerror =
function () {
deferred.reject(
new
Error(req.statusText));
};
req.send();
var abort =
function () {
if (req.readyState !== XMLHttpRequest.UNSENT) {
req.abort();
}
};
return {
promise: deferred.promise,
abort: abort
}
}
ajaxGet(
'd1.json').promise
.then(
res=>
JSON.parse(res))
.catch(
err=>
console.log(err))
.then(
res=>
console.log(res))
|
异步请求的超时处理
发起一个异步请求,如果3s内还没有请求成功,则取消请求
var getData = ajaxGet(
'd1.json');
console.log(
'显示loading')
MPromise.timeout(getData.promise,
3000)
.then(
res=>{
return
JSON.parse(res);
}, err=>{
if(err
instanceof
window.TimeoutError){
getData.abort();
throw
new
Error(
'请求超时,取消请求')
}
else{
throw err;
}
})
.then(
res=>{
console.log(
'请求成功: ', res); })
.always(
()=>{
console.log(
'取消loading') })
.catch(
err=>{
console.log(err) })
|
基于Promise的fs方法链
使用Promise对fs封装的好处是可以很方便的异步处理文件流,对错误可以集中式处理,如果是同步使用fs相关方法,错误处理将会变得复杂
var fs =
require(
"fs");
var MPromise =
require(
'./promise_browser');
function File() {
this.promise = MPromise.resolve();
}
File.read =
function (filePath) {
var file =
new File();
return file.read(filePath);
};
File.prototype.then =
function (onFulfilled, onRejected) {
this.promise =
this.promise.then(onFulfilled, onRejected);
return
this;
};
File.prototype[
"catch"] =
function (onRejected) {
this.promise =
this.promise.catch(onRejected);
return
this;
};
File.prototype.read =
function (filePath) {
return
this.then(
function () {
return fs.readFileSync(filePath,
"utf-8");
});
};
File.prototype.transform =
function (fn) {
return
this.then(fn);
};
File.prototype.write =
function (filePath) {
return
this.then(
function (data) {
return fs.writeFileSync(filePath, data)
});
};
module.exports = File;
|
使用
var File =
require(
"./fs-promise-chain");
var inputFilePath =
"input.txt",
outputFilePath =
"output.txt";
File.read(inputFilePath)
.transform(
function (content) {
return
">>" + content;
})
.write(outputFilePath)
.catch(
function(err){
console.log(err)
})
|
对于 nodejs 相关的异步处理,Q
或 bluebirde
都有相关的API用于包装成Promise对象,
例如 bluebirde
中可以使用 Promise.promisify
方法进行包装
var readFile =
Promise.promisify(
require(
"fs").readFile);
readFile(
"myfile.js",
"utf8").then(
function(contents) {
return
eval(contents);
}).then(
function(result) {
console.log(
"The result of evaluating myfile.js", result);
}).catch(
SyntaxError,
function(e) {
console.log(
"File had syntax error", e);
}).catch(
function(e) {
console.log(
"Error reading file", e);
});
|
这里还是建议大家使用 Q
或者 bluebirde
等相关成熟的Promise方案,本系列的Promise学习就到这里的,在此过程中封装的Promise工具类也主要用于学习和理解Promise的原理。
重点是我们掌握了Promise的使用之后,能更好的和ES7 的 Async/await
结合起来使用,那么以后的异步处理就更加得心应手了。
据说 node7.0 已经支持 Async/await
,让我们视目以待吧。
相关示例源码,请至 MPromise 查看
http://coderlt.coding.me/2016/12/05/promise-in-depth-an-introduction-3/