promise
1 介绍
promise是一种异步编程方案,es6的一个
异步任务主要体现在ajax,数据库,计时器,fs文件操作
也是一种解决回调地狱的方案,回调地狱也就是函数套娃,解决回调地狱的还有一种es6里的生成器函数yield
在这里回顾一下
function getUsers(){
setTimeout(() =>{
let data = "用户数据";
iterator.next(data);
}, 1000)
}
function getOrders(){
setTimeout(() =>{
let data = "订单数据";
iterator.next(data);
}, 1000)
}
function getGoods(){
setTimeout(() =>{
let data = "商品数据";
iterator.next(data);
}, 1000)
}
function * gen(){
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
let iterator = gen();//这里将gen实例化,好像是必须实例化,才能在第一个函数中使用next
iterator.next();
2 初体验
promise reject value reson这些名字都是根据习惯来的,不一定要是这个
promise是个构造函数,resolve reject是两个函数
then 用来定义函数内容
let a = 2
let promise = new Promise((resolve, reject)=>{
if(a > 30){
resolve(a);
}
else{
reject(a);
}
}
)
promise.then((value)=>{
console.log("resolve");
}, (reson)=>{
console.log("reject");
})
3 用promise来写fs
const fs = require("fs");
const promise = new Promise((resolve, reject)=>{
fs.writeFile("./content.txt", "我是一个大聪明", "utf-8", (error, data)=>{
if(error){
reject(error);
}
else{
resolve(data);
}
})
})
promise.then(value=>{
console.log(value);
}, reason=>{
console.log(reason);
})
let promise2 = new Promise((resolve, reject) => {
fs.readFile("./content.txt", "utf-8", (error, data)=>{
if(error){
resolve(error);
}
else{
reject(data);
}
})
})
promise2.then(value=>{
console.log(value);
}, reason=>{
console.log(reason);
})
3.1 封装
//封装函数 参数为路径,返回值为promise对象
function mineReadFile(path){
return new Promise((resolve, reject) => {
require("fs").readFile(path, "utf-8", (error, data) => {
if(error){
reject(error);
}
else{
resolve(data);
}
})
})
}
//调用
mineReadFile("./content.txt").then(value=>{
console.log(value);
}, reason=>{
console.log(reason);
})
这个封装就是将回调函数风格转化成promise风格的函数,而util里的promisify本身就能够实现这个功能
不同的是这个没有指定编码方式,所以传进来的内容需要用到toString()转化成字符串形式
mineReadFile = require("util").promisify(require("fs").readFile);
mineReadFile("./content.txt").then(value=>{
console.log(value.toString());
}, reason=>{
console.log(reason);
})
4 promise写AJAX
<button>点击我获取数据</button>
let btn = document.querySelector("button");
btn.addEventListener("click", ()=>{
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:8000/temp");
xhr.send();
xhr.responseType = "json";
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log(xhr.response.name);
}
else{
console.log(xhr.status);
}
}
}
})
4.1 原生
服务端 node.js
const express = require("express");
const app = express();
app.get("/temp", (request, response) => {
response.setHeader("Access-Control-Allow-Origin", "*");
let data = {
name : "iceylia",
age: 80
}
data = JSON.stringify(data);
response.send(data);
})
app.listen(8000, ()=>{
console.log("8000端口已经启用");
})
4.2 promise
let btn = document.querySelector("button");
btn.addEventListener("click", ()=>{
const promise = new Promise((resolve, reject)=>{
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:8000/temp");
xhr.send();
xhr.responseType = "json";
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response);
}
else{
reject(xhr.status);
}
}
}
})
promise.then(value=>{
console.log(value.name);
}, reason=>{
console.log(reason);
});
})
4.3 封装
function sendAJAX(url){
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send();
xhr.responseType = "json"
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(this.status >= 200 && this.status < 300){
resolve(this.response);
}
else{
reject(thsi.status);
}
}
}
})
}
sendAJAX("http://127.0.0.1:8000/temp").then(value=>{
console.log(value.name);
}, reason => {
console.log(reason);
})
5 promise实例化属性
下面的属性都是内置的,只读的,无法通过js直接获取
Promise本身就是一个构造函数,也就是在实例化函数对象的时候需要传参,这个参数就是一个函数对象resolve和reject的函数,这个函数被称为执行器(executor)函数,会同步进行,不会到异步队列里面去,也就是直接执行
promise.then()是一个两个参数的实例化方法,就是定义resolve和reject
promise.catch()是用then对失败时的一个单独的封装,用法和then差不多,promise.catch(reason=>{console.log(reason});
5.1 PromiseState
pending是初始化的,未被定义的
resolved 就是成功,成功的数据一般就是value
rejected就是失败,reason
promisestate只能变化一次,就是从pending变为resolved或者是rejected
5.2 PromiseResult
异步任务执行的结果,就是无论成功还是失败,value和reason都保存在这个属性里面
6 promsie的流程
7 Promise函数对象
7.1 Promise.resolve()
这个函数的返回值是一个promise对象,如何里面的参数不是promise对象,那么返回的就是一个成功的promise对象,PromiseState为resolved,如果是一个promise对象,那么这个对象是一个成功的,返回的就是一个成功的。否则就是一个失败的对象,而PromiseResult的值就是这个函数的参数
比如
let p1 = Promise.resolve("fa");
console.log(p1);
let p2 = Promise.resolve(new Promise((resolve, reject) => {
reject("errror");
}));
p2.catch(reason => {
console.log(reason);
});
7.2 Promise.reject()
这个函数就是无论如何结果都是一个失败的promise对象,到现在我还不明白这个函数的意义
7.3 Promise.all()
参数是多个promise对象,只有全部成功才会返回一个成功的promise对象
let p1 = Promise.resolve("faaf");
let p2 = Promise.resolve("success");
let p3 = Promise.resolve("faf");
let p4 = Promise.all([p1, p2, p3]);
console.log(p4);
7.4 Promise.race()
这个函数就有意思了,参数也是promise数组,结果就是最先完成的promise对象的结果
8 Promise关键问题
如何修改promise对象的状态?调用resolve,reject或者使用throw
对一个promise对象使用多个then,都会执行吗? 会
是先定义then还是先执行resolve和reject改变状态? 当Promise构造函数是一个同步任务的时候,就是先改变状态,异步是先指定then
promise对象的结果是什么?有then执行的回调函数的返回值决定的,then返回的是一个promise对象,而如果执行的是value,然后value那个函数最后是throw,那么就是rejected的promise对象,如果最后是return new Promise()这样的return,那么结果就和return差不多,return 1就是一个resolved的promise对象
promise如何串联多个异步任务,就是promise.then(value=>{return new Promise()}).then();这就是then的链式调用
什么是异常穿透? 在使用then的链式调用的时候,就可以在最后面加上一个catch()比如promise.then().then().catch(reason=>{});这样当你每一次的失败函数是一样的时候,就不需要每个then都使用两个回调函数,一个最后面的catch即可
如何中断promise链?返回一个pending的promise对象,这样后续的then都不会执行了,比如promise.then(value=>{return new Promise()}).then();
9 自定义promise
9.1 初始化结构,能通过resolve和reject改变result和state
function Promise(executor){
const self = this;
this.PromiseState = "pending";
this.PromiseResult = "";
function resolve(data){
if(self.PromiseState !== pending) return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
}
function reject(data){
if(self.PromiseState !== pending) return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
}
executor(resolve, reject);
}
引入
```html
<script src="promise.js"></script>
<script>
let p = new Promise();
p.then();
</script>
直接覆盖原本的promise对象
excutor是执行器函数, prototype是原型的意思,也就是实例化的函数对象。
9.2 executor中的throw
如果在executor里写了throw,要抛出错误,就需要写try catch
let p = new Promise ((resolve, reject))=>{
throw "error";
})
try{
executor(resolve, reject);
}catch(err){
reject(err);
}
9.3 then根据state用回调
onResolved和onRejected都是用户在使用then的时候自定义的两个回调函数
then要做的就是根据executor改变的PromiseState来决定调用哪一个
Promise.prototype.then = function(onResolved, onRejected){
if(this.PromiseState === "fulfilled"){
onResolved(this.PromiseResult);
}
if(this.PromiseState === "rejected"){
onRejected(this.PromiseResult);
}
}
9.4 executor是异步函数时的回调
let p = new Promise((resolve, reject) => {
setTimeout((resolve("ok"))=>{}, 100);
});
可以看到因为executor是异步的,所以没有执行resolve或reject,而是先执行的then,这个时候PromiseState都还没改变,也就不会执行回调
function Promise(executor){
const self = this;
this.PromiseState = "pending";
this.PromiseResult = "";
this.callbacks = [];
function resolve(data){
if(self.PromiseState !== pending) return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
self.callbacks.forEach(item=>{
item.onResolved(data);
});
}
function reject(data){
if(self.PromiseState !== pending) return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
self.callbacks.forEach(item=>{
item.onRejected(data);
});
}
try{
executor(resolve, reject);
}catch(err){
reject(err);
}
}
Promise.prototype.then = function(onResolved, onRejected){
if(this.PromiseState === "fulfilled"){
onResolved(this.PromiseResult);
}
if(this.PromiseState === "rejected"){
onRejected(this.PromiseResult);
}
if(this.PromiseState === "pending"){
this.callbacks.push({
onRejected,
onResolved
});
}
}
这里直接用一个属性callbacks来结束回调,在执行resolve和reject的时候逐一调用,用数组也时为了如果此时有多个then方法
9.5 then的返回值
如果用户在使用then定义返回值的时候使用了return,那么就接收
function Promise(executor){
const self = this;
this.PromiseState = "pending";
this.PromiseResult = "";
this.callbacks = [];
function resolve(data){
if(self.PromiseState !== "pending") return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
self.callbacks.forEach(item=>{
item.onResolved(data);
});
}
function reject(data){
if(self.PromiseState !== "pending") return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
self.callbacks.forEach(item=>{
item.onRejected(data);
});
}
try{
executor(resolve, reject);
}catch(err){
reject(err);
}
}
Promise.prototype.then = function(onResolved, onRejected){
const self = this;
return new Promise((resolve, reject) => {
if(self.PromiseState === "fulfilled"){
try{
let result = onResolved(self.PromiseResult);
if(result instanceof Promise){//如果result是一个Promise对象,那么其本身的state已经定了,没有异步问题
result.then((value)=>{
resolve(value);
}, reason=>{
reject(reason);
});
}
else{
resolve(result);
}
}catch(err){
reject(err);
}
}
if(self.PromiseState === "rejected"){
try{
let result = onRejected(self.PromiseResult);
if(result instanceof Promise){//如果result是一个Promise对象,那么其本身的state已经定了,没有异步问题
result.then((value)=>{
resolve(value);
}, reason=>{
reject(reason);
});
}
else{
resolve(result);
}
}catch(err){
reject(err);
}
}
if(self.PromiseState === "pending"){
self.callbacks.push({
onRejected: function(){
try{
let result = onRejected(self.PromiseResult);
if(result instanceof Promise){//如果result是一个Promise对象,那么其本身的state已经定了,没有异步问题
result.then((value)=>{
resolve(value);
}, reason=>{
reject(reason);
});
}
else{
resolve(result);
}
}catch(err){
reject(err);
}
},
onResolved: function(){
try{
let result = onResolved(self.PromiseResult);
if(result instanceof Promise){//如果result是一个Promise对象,那么其本身的state已经定了,没有异步问题
result.then((value)=>{
resolve(value);
}, reason=>{
reject(reason);
});
}
else{
resolve(result);
}
}catch(err){
reject(err);
}
}
});
}
});
}
9.6 catch与异常穿透
catch就是对then里面的onRejected的单独封装,所以什么功能都可以直接交给then来处理,返回值也是
Promise.prototype.catch = function(onRejected){
return this.then(undefined, onRejected);
}
但是这样的话then里面的onResolved就是undefined,会报错,所以我们需要在then中加上
if(typeof onResolved !== "function"){
onResolved = value => value;
}
异常穿透就是链式的then中只用写一个onResolved,只需要在then中加上
if(typeof onResolved !== "function"){
onRejected = reason => {
throw reason;
}
}
当我们使用链式的then没写onResolved的时候,就会用这个自动补上,下面是一个异常穿透的代码
p3.then(value=>{
console.log("get users");
}).then(value=>{
throw "error";
}).catch(reason=>{
console.log(reason);
})
9.7 构造函数的四个方法
Promise.resolve = function(value){
return new Promise((resolve, reject) => {
if(value instanceof Promise){
value.then(v => {
resolve(v);
}, r => {
reject(r);
})
}
else{
reject(value);
}
});
}
Promise.reject = function(reason){
return new Promise((resolve, reject) => {
reject(reason);
});
}
Promise.all = function(promises){
return new Promise((resolve, reject) => {
let count = 0;
let arr = [];
for(let i = 0; i < promises.length; i++){
promises[i].then(value => {
count++;
arr[i] = value;
if(count === promises.length){
resolve(arr);
}
}, reason => {
reject(reason);
});
}
});
}
Promise.race = function(promises){
return new Promise((resolve, reject) => {
for(let i = 0; i < promises.length; i++){
promises[i].then(value => {
resolve(value);
}, reason => {
reject(reason);
})
}
});
}
9.8 class类的封装
class Promise{
constructor(executor){
const self = this;
this.PromiseState = "pending";
this.PromiseResult = "";
this.callbacks = [];
function resolve(data){
if(self.PromiseState !== "pending") return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
setTimeout(() => {
self.callbacks.forEach(item=>{
item.onResolved(data);
});
});
}
function reject(data){
if(self.PromiseState !== "pending") return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
setTimeout(() => {
self.callbacks.forEach(item=>{
item.onRejected(data);
});
});
}
try{
executor(resolve, reject);
}catch(err){
reject(err);
}
}
then(onResolved, onRejected){
const self = this;
if(typeof onResolved !== "function"){
onResolved = value => value;
}
if(typeof onResolved !== "function"){
onRejected = reason => {
throw reason;
}
}
return new Promise((resolve, reject) => {
function callback(type){
try{
let result = type(self.PromiseResult);
if(result instanceof Promise){//如果result是一个Promise对象,那么其本身的state已经定了,没有异步问题
result.then((value)=>{
resolve(value);
}, reason=>{
reject(reason);
});
}
else{
resolve(result);
}
}catch(err){
reject(err);
}
}
if(self.PromiseState === "fulfilled"){
setTimeout(() => {
callback(onResolved);
});
}
if(self.PromiseState === "rejected"){
setTimeout(() => {
callback(onRejected);
});
}
if(self.PromiseState === "pending"){
self.callbacks.push({
onRejected: function(){
callback(onRejected);
},
onResolved: function(){
callback(onResolved);
}
});
}
});
}
catch(onRejected){
return this.then(undefined, onRejected);
}
static resolve(value){
return new Promise((resolve, reject) => {
if(value instanceof Promise){
value.then(v => {
resolve(v);
}, r => {
reject(r);
})
}
else{
reject(value);
}
});
}
static reject(reason){
return new Promise((resolve, reject) => {
reject(reason);
});
}
static all(promises){
return new Promise((resolve, reject) => {
let count = 0;
let arr = [];
for(let i = 0; i < promises.length; i++){
promises[i].then(value => {
count++;
arr[i] = value;
if(count === promises.length){
resolve(arr);
}
}, reason => {
reject(reason);
});
}
});
}
static race(promises){
return new Promise((resolve, reject) => {
for(let i = 0; i < promises.length; i++){
promises[i].then(value => {
resolve(value);
}, reason => {
reject(reason);
})
}
});
}
}
10 async与await
10.1 介绍
async就是异步的意思,我们可以将一个异步任务当成同步的任务来执行,比一般的回调函数更为简洁好用,在实现这一功能之前,我们先要了解这两个关键字的基本特点
10.2 async
async就是一种申明函数的关键字,也就是将一个函数申明为异步的函数。
async functiuon sss(){}
sss();
10.3 await
- await必须使用在异步函数里面
- 右侧一般是promise对象,也可以是一般的值
- 如果是promise对象,返回值就是promise成功的值,如果是一个失败的promise对象,那么我们需要使用try catch来接收这个失败的值
- 如果是一般的值,那么返回结果就是这个值
10.4 实战
这里使用了ajax的封装函数,可以回到上面再看一下,似乎axios也有这个封装函数
let btn = document.querySelector("button");
btn.addEventListener("click", async () => {
try{
let content = await sendAJAX("http://127.0.0.1:8000/temp");
btn.innerHTML = content.name;
}
catch(error){
console.warn(error);
}
});
而原本的promise.then的写法就是
btn.addEventListener("click", () => {
sendAJAX("http://127.0.0.1:8000/temp").then(value=>{
console.log(value.name);
}, reason => {
console.log(reason);
})
}
似乎也没有怎么简便