碎碎念
本质上,Promise 是一个绑定了回调的对象,而不是将回调传进函数内部。它代表了一个异步操作的最终完成或者失败。
node读取文件
我们知道readFile API是异步操作,如果我们想依次读取文件的话,需要嵌入
const fs = require('fs')
const path = require('path')
function readTxt(dir,callback){
fs.readFile(path.join(__dirname,dir),'utf8',(err,data)=>{
if(err) throw err;
callback(data)
})
}
readTxt('01.txt',function (data) {
console.log(data)
readTxt('02.txt',function (data) {
console.log(data)
readTxt('03.txt',function (data) {
console.log(data)
})
})
})
这种代码可读性不强,而且如果阅读更多文件的话,就是回调地狱,十分混乱,那怎样解决这个异步操作?如果使用return,那么后续的代码不会继续执行;而Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
了解Promise
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。–阮一峰
Promise对象两个特点
- 对象的状态不受外界的影响。Promise代表一个异步操作,有三种状态:(1)pending进行中(2)fufilled已成功(代码中一般参数用
resolved
)(3)rejected已失败。只有异步操作的结果,可以决定是哪种状态;任何其它操作到无法改变这个状态;这也是Promise
名字的由来;它的英语意思是“承诺”,意思是任何手段都改变不了 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象状态的改变,只有两种可能;一种是pending到fufilled;一种是pending到rejected;只要这两种情况发生,状态就已经凝固,不会再变了,会一直保持这个结果。这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
使用Promise上面的回调地狱
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
如何使用
- Promise是一个构造函数,既然是一个构造函数,那么可以使用
new Promise()
得到一个实例 - 在Promise上分别有两个函数,一个是resolve(成功之后的回调);一个是reject(失败之后的回调函数)
- 在Promise的构造函数的prototype原型上,有一个
then()
方法,只要是Promise的实例对象都可以访问到·
var promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(value) {
// failure
});
解决文件的层层嵌套
const fs = require('fs')
const path = require('path')
function readTxt(dir){
return new Promise(function(resolve,reject){
fs.readFile(path.join(__dirname,dir),'utf8',(err,data)=>{
if(err){
// pending到fufilled,状态已经凝固不会再发生改变,会一直保持这个结果
reject(err)
}else{
resolve(data)
}
})
})
}
readTxt('01.txt')
.then(function (data) {
console.log(data)
return readTxt('02.txt')
},function (err) {
console.log(err)
// 采用箭头函数
})
.then((data)=>{
console.log(data)
return readTxt('03.txt')
})
.then((data)=>{
console.log(data)
})
//如果前面有任何Promise失败,则立即终止执行,并马上进入catch处理Promise抛出异常
.catch((err)=>{
throw err;
})
描述和方法
Promise是一个代理对象(代理一个值),被代理的值在Promise可能是未知的;它允许你为异步操作的成功和失败分别绑定相应的处理方法;这让异步操作可以像同步方法那样返回值;但不是立即返回执行的结果;而是一个能代表未来出现的结果的Promise对象
pending状态的方法的Primise对象可能会变fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。
使用setTime来模拟
let myFirstPromise = new Promise(function(resolve, reject){
//当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
//在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
setTimeout(function(){
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function(successMessage){
//successMessage的值是上面调用resolve(...)方法传入的值.
//successMessage参数不一定非要是字符串类型,这里只是举个例子
console.log("Yay! " + successMessage);
});
原型上的API
Promise.prototype.catch(onRejected)
添加一个拒绝(rejection) 回调到当前 promise, 返回一个新的promise。一般来说作用是:如果有任何的 Promise 执行失败,则立即终止所有 promise 的执行,并 马上进入 catch 去处理 Promise中 抛出的异常
Promise.prototype.then(onFulfilled, onRejected)
添加解决(fulfillment)和拒绝(rejection)回调到当前 promise, 返回一个新的 promise, 将以回调的返回值来resolve.
Promise.prototype.finally(onFinally)
添加一个事件处理回调于当前promise对象,并且在原promise对象解析完毕后,返回一个新的promise对象。回调会在当前promise运行完毕后被调用,无论当前promise的状态是完成(fulfilled)还是失败(rejected)
让函数具有promise功能
我们平时用到的时候,一般是异步请求用得到,所以我们使用XML为例子
- 使用node核心服务http模块来搭建web服务器(这里我们使用express框架来搭建)
cnpm init -y
cnpm i express body-parser -S
- 后台服务器
const express = require('express');
const app = express();
const bodyParser = require('body-parser')
// 挂载内置中间件
app.use(express.static('public'));
// 挂载参数处理中间件(post)
app.use(bodyParser.urlencoded({ extended: false }));
// 处理json格式的参数
app.use(bodyParser.json());
// 处理get提交参数
app.get('/login',(req,res)=>{
res.send('get data');
});
app.get('/register',(req,res)=>{
res.send('register');
});
app.listen(3000,()=>{
console.log('running...');
});
启动我们的服务器
3. 静态资源(新建public文件夹,在public新建05login.html)
<button>请求服务端数据</button>
<script>
let btn = document.querySelector('button')
btn.onclick = function () {
const xhr = new XMLHttpRequest()
xhr.open('get', 'http://localhost:3000/login');
xhr.onload = function (data) {
console.log(data.currentTarget.responseText)
};
xhr.send();
}
</script>
打开login.html页面
4. 让异步请求拥有promise的功能,只需要返回一个promise即可
function myAsyncFunction(url){
return new Promise((resolve,reject)=>{
let xhr = new XMLHttpRequest()
xhr.open('get',`http://localhost:3000${url}`)
xhr.onload = () => resolve(xhr.responseText)
xhr.onerror = () => reject(xhr.statusText)
xhr.send()
})
}
btn.onclick = function () {
myAsyncFunction('/login')
.then(function (data) {
console.log(data)
})
.catch(function (err) {
throw err;
})
}
参考文档
Promise 对象 阮一峰
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise