去年暑假的时候就学了Promise,最近在学node的时候又遇到了再复习一遍,写个博客缝合一波阮佬的ES6标准入门与红宝书中的内容,用我自己的白话给大家讲一下。
所谓Promise简单来说就是一个容器,里面保存着未来才会结束的事件(通常是异步操作)的结果。从语法上来说,Promise是一个对象,从它可以获取异步操作的消息
promise是一个有状态的对象(状态机)
(1)对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
新建的Promise就是一个容器,里面可以存放异步操作,也可以存放同步操作
const fs = require("fs");
const path = require("path");
let filePath = path.join(__dirname, "files", "3.txt");
// 异步操作可能成功或者失败
// 第一个形参resolve , 成功的时候执行的函数
// 第二个形参reject , 失败的时候执行的函数
let p1 = new Promise((resolve, reject)=>{
//1、同步代码
console.log("同步代码");
// pending(进行中)、fulfilled(已成功)和rejected(已失败)
//2、异步代码
fs.readFile(filePath,"utf-8",(error1, data1)=>{
if(error1){
//失败的时候做的事情
reject(error1); // 调用后面p1.then的第二个函数
}
//读取完之后做的事情
resolve(data1); // 调用后面p1.then的第二个函数
})
});
p1.then((data1)=>{
console.log("读取成功", data1);
},(error1)=>{
console.log("读取失败", error1);
});
then方法定义在原型对象Promise,prototype上,它的作用是为Promise实例添加状态改变时的回调函数。then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Reject状态的回调函数。
then方法返回的是一个新的Promise实例(注意!不是原来的那个Promise实例),因此可以采用链式写法,即then方法后再调用另一个then方法。
链式调用的特点:
const fs = require("fs");
const path = require("path");
let filePath = path.join(__dirname, "files", "3.txt");
// 新建的Promise就是一个容器,里面可以存放异步操作,也可以存放同步操作
// 异步操作可能成功或者失败
// 第一个形参resolve , 成功的时候执行的函数
// 第二个形参reject , 失败的时候执行的函数
let p1 = new Promise((resolve, reject)=>{
//1、同步代码
// console.log("同步代码");
// pending(进行中)、fulfilled(已成功)和rejected(已失败)
//2、异步代码
fs.readFile(filePath,"utf-8",(error1, data1)=>{
if(error1){
//失败的时候做的事情
reject(error1)
}
//读取完之后做的事情
resolve("resolve的形参")
})
});
//业内把promise then的写法被称为开火车开发
p1.then((data1)=>{
return p1
},(error1)=>{
console.log("读取失败", error1);
return error1
}).then((data)=>{
console.log(data); // “resolve("resolve的形参")
});
Promise.prototype.catch方法是.then(null,rejection)的别名,用于指定发生错误时的回调函数。
如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中
Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前两个回调函数的错误
});
//一般,我们会把以下代码:
p1.then((data1)=>{
console.log("承诺成功", data1);
},(error1)=>{
console.log("承诺失败", error1);
});
//写成
p1.then((data1)=>{
console.log("承诺成功", data1);
}).catch((error1)=>{
console.log("承诺失败", error1);
}).finally(()=>{
console.log("承诺成功与失败都会执行这里的代码");
});
Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
var p = Promise.all([p1,p2,p3]);
上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)
p 的状态由 p1、p2、p3 决定,分成两种情况。
Promise.all([p1,p2]).then((data)=>{
// data是一个数组,分别是p1和p2最后传来的数据
console.log(data); // [ '我', '爱' ]
console.log(data.join("")); // 我爱
}).catch((error)=>{
//只要执行p1,p2时其中一个报错,就会执行这里的代码
console.log(error);
});
Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
var p = Promise.race([p1,p2,p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。
如果Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。
const fs = require("fs")
const path = require("path")
const util = require('util');
let filePath1 = path.join(__dirname, "files", "1.txt")
let filePath2 = path.join(__dirname, "files", "2.txt")
let readFilePromise = util.promisify(fs.readFile);
let p1 = readFilePromise(filePath1,"utf-8")
let p2 = readFilePromise(filePath2,"utf-8")
Promise.race([p1,p2]).then((data)=>{
// p1,p2只要其中一个执行完,就会执行一遍这里的代码,且这里的代码只会执行1次
console.log(123);
console.log(data);
});
//123
//我
有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将 jQuery 生成 deferred 对象,转为一个新的 ES6 的 Promise 对象。
如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。
var p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数。
var p = Promise.reject('出错了');
p.then(null, function (s){
console.log(s)
});
// 出错了
上面代码生成一个Promise对象的实例,状态为rejected,回调函数会立即执行。