初始Promise【Promise】

Promise

1. Promise是什么?

1.1 理解

  1. 抽象表达:
    (1)Promise是一门新的技术(ES6规范)
    (2)Promise是JS中进行异步编程的新解决方案
  2. 具体表达
    (1)从语法上来说:Promise是一个构造函数
    (2)从功能上来说:promise对象用来封装一个异步操作并可以获取其成功/失败的结果值

1.2 promise的状态改变

创建promise对象(pending状态)

const p = new Promise(executor);

其中:

executor函数: 执行器 (resolve, reject) => {}

resolve函数: 内部定义成功时我们调用的函数 value => {}

reject函数: 内部定义失败时我们调用的函数 reason => {}

executor会在Promise内部立即同步调用,异步操作在执行器中执行

实例对象调用Promise原型中的then方法来完成对结果的处理

<script>
    const p = new Promise((resolve, reject) => {
        //如果咱们公司今年挣钱了,年底就发奖金,否则不发
        resolve('ok');
    })
    console.log(p)
    p.then(() => {
        console.log('发奖金')
    }, () => {
        console.log('不发奖金')
    })
</script>

2. 为什么要用Promise?

2.1 指定回调函数的方式更加灵活

  1. 旧的:必须在启动异步任务前指定
  2. promise:启动异步任务=>返回promise对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定多个)

2.2 支持链式调用,可以解决回调地狱问题

  1. 什么是回调地狱?

回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件

  1. 回调地狱的缺点?

不便于阅读;不便于异常处理

  1. 解决方案?

promise链式调用

  1. 终极解决方案?

async/await

3. Promise实例对象的两个属性

3.1 PromiseState

此属性为promise对象的状态属性。

  • fulfilled:成功的状态
  • rejected:失败的状态
  • pending:初始化的状态

【注】状态只能由pending->fulfilled 或者是 pending->rejected

3.2 PromiseResult

此属性为promise对象的结果值(resolve以及reject函数的形参值)

4. resolve函数以及reject函数

  • resolve:修改promise对象的状态,由pending修改到fulfilled;将实参设置到这个属性PromiseResult中。
  • reject:修改promise对象的状态,由pending修改到rejected;将实参设置到这个属性PromiseResult中。

案例1:利用promise来进行读取文件操作

//1.普通文件读取方式
const fs = require('fs');

//2.直接利用readfile来进行读取
/* fs.readFile(__dirname + '/data.txt',(err,data)=>{
    if(err) throw err;
    console.log(data.toString());
}) */

//3.利用promise来实现文件的读取
const p = new Promise((resolve, reject) => {
    fs.readFile(__dirname + '/data.txt', (err, data) => {
        if (err) {
            reject(err);
        }else{
            resolve(data);
        }
    })
}); 

p.then(value=>{
    console.log(value.toString());
},reason=>{
    console.log(reason);
})

案例2:利用promise进行ajax请求

<body>
    <button>发送ajax请求</button>
    <script>
        //1.获取DOM元素对象
        let btn = document.querySelector('button');
        //2.绑定事件
        btn.onclick = function(){
            //3.创建promise实例对象
            const p = new Promise((resolve,reject)=>{
                //4.创建ajax实例对象
                const xhr = new XMLHttpRequest();
                //5.打开请求
                xhr.open('get','https://www.yiketianqi.com/free/day?appid=82294778&appsecret=4PKVFula&unescape=1');
                //6.发送请求
                xhr.send();
                //7.利用onreadystatechange事件
                xhr.onreadystatechange = function(){
                    //8.判断
                    if(xhr.readyState == 4){
                        if(xhr.status == 200){
                            resolve(xhr.responseText);
                        }else{
                            reject(xhr.response);
                        }
                    }
                }
            });
            p.then(value=>{
                console.log(JSON.parse(value));
            },reason=>{
                console.log('获取信息失败');
            })
        }
    </script>
</body>

案例3:利用promise进行数据库操作

const mongoose = require('mongoose');

new Promise((resolve, reject) => {
    mongoose.connect('mongodb://127.0.0.1/project');
    mongoose.connection.on('open', ()=>{
        //连接成功的情况
        resolve();
    });

    mongoose.connection.on('error', () => {
        //连接失败的情况
        reject();
    })
}).then(value => {
    //创建结构
    const NoteSchema = new mongoose.Schema({
        title: String,
        content: String
    })
    //创建模型
    const NoteModel = mongoose.model('notes', NoteSchema);

    //读取操作
    NoteModel.find().then(value => {
        console.log(value);
    }, reason => {
        console.log(reason);
    })
}, reason => {
    console.log('连接失败');
})

案例4:封装一个函数,作用是读取文件

const fs = require('fs');

function ReadFileFun(path){
    return new Promise((resolve,reject)=>{
         fs.readFile(path,(err,data)=>{
              //判断
              if(err){
                    reject(err)
              }else{
                    resolve(data);
              }
         })
    });
}

ReadFileFun('./data.txt').then(value=>{
    console.log(value.toString());
},reason=>{
    console.log(reason);
})

node中的promisify

  • promisify (只能在 NodeJS 环境中使用)
  • promisify 是 util 模块中的一个方法 util 是 nodeJS 的内置模块
  • 作用: 返回一个新的函数, 函数的是 promise 风格的.
const util = require('util');
const fs = require('fs');//通过 fs.readFile 创建一个新的函数
const mineReadFile = util.promisify(fs.readFile);​mineReadFile('./resource/2.html')
.then(value => {    
	console.log(value.toString());
	}, reason => {    
	console.log(reason);
	})

5. Promise对象的状态

Promise对象通过自身的状态来控制异步操作,Promise实例具有三种状态.

  • 异步操作未完成:pending
  • 异步操作成功:fulfilled
  • 异步操作失败:rejected

这三种的状态的变化途径只有两种

  • 从pending(未完成)到fulfilled(成功)
  • 从pending(未成功)到rejected(失败)

一旦状态发生变化,就凝固了,不会再有新的状态变化,这也是Promise这个名字的由来,它的英语意思"承诺",

一旦承诺生效,就不得再改变了,这也意味着Promise实例的状态变化只可能发生一次。

在Promise对象的构造函数中,将一个函数作为第一个参数。而这个函数,就是用来处理Promise的状态变化。

上面的resolve和reject都为一个函数,他们的作用分别是将状态修改为resolved和rejected。

因此,Promise的最终结果只有两种。

异步操作成功,Promise实例传回一个值(value),状态变为fulfilled.
异步操作失败,Promise实例抛出一个错误(error),状态变为rejected

6. Promise的then方法

then:指定用于得到成功value的成功回调和用于得到失败reason的失败回调,返回一个新的promise对象

  • 成功的状态:执行第一个回调函数
  • 失败的状态:执行第二个回调函数

promise.then()返回的新promise的结果状态由什么决定?

(1) 简单表达: 由then()指定的回调函数执行的结果决定

(2) 详细表达:

① 如果抛出异常, 新promise变为rejected, reason为抛出的异常

const p = new Promise((resolve,reject)=>{
     resolve('ok');
});

let result = p.then(value=>{
	throw '错误';
},reason=>{
	console.log(reason);
});

console.log(result);

② 如果返回的是非promise的任意值, 新promise变为fulfilled, PromiseResult为返回的值

const p = new Promise((resolve,reject)=>{
                resolve('ok');
});

let result = p.then(value=>{
	return 100;
},reason=>{
	console.log(reason);
});

console.log(result);

③ 如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果

const p = new Promise((resolve,reject)=>{
                resolve('ok');
});

let result = p.then(value=>{
	return new Promise((resolve,reject)=>{
		//resolve('111');
        reject('error');
	})
},reason=>{
	console.log(reason);
});

console.log(result);

7. Promise的链式调用

const p = new Promise((resolve,reject)=>{
	//resolve('ok');
    reject('error');
});

p.then(value=>{
	console.log(value);
},reason=>{
	console.log(reason);
}).then(value=>{
	console.log(value);
},reason=>{
	console.log(reason);
})

案例:通过promise的链式调用来读取文件

回调地狱的方式:

const fs = require('fs');
fs.readFile('./resource/1.html',(err,data1)=>{
    if(err) throw err;
    fs.readFile('./resource/1.html',(err,data2)=>{
    	if(err) throw err;
        fs.readFile('./resource/1.html',(err,data3)=>{
    		if(err) throw err;
            console.log(data1 + data2 + data3);
		})
	})
})

Promise的形式:

需求:读取resource下三个文件内容,并在控制台合并输出

new Promise((resolve,reject)=>{
	fs.readFile('./resource/1.html',(err,data)=>{
		 //如果失败 则修改promise对象状态为失败
        if(err) reject(err);
        //如果成功 则修改promise对象状态为成功
        resolve(data);
	})
}).then(value=>{
    return new Promise((resolve,reject)=>{
        fs.readFile('./resource/2.html',(err,data)=>{
             //失败
            if(err) reject(err);
            //成功
            resolve([value,data]);
        })
	})
}).then(value=>{
    return new Promise((resolve,reject)=>{
        fs.readFile('./resource/3.html',(err,data)=>{
             //失败
            if(err) reject(err);
            value.push(data);
            //成功
            resolve(value);
        })
	})
}).then(value=>{
    console.log(value.join(""));
})

8. Promise下的几种方法

8.1 Promise.resolve()

将一个普通值转化为promise类型的数据

  • 若参数为非promise对象,则返回的结果为成功状态的promise对象
let p1 = Promise.resolve(123);
console.log(p1);
let p2 = Promise.resolve(undefined);
console.log(p2);
  • 若参数为promise对象,参数的状态决定返回结果的状态
let p3 = Promise.resolve(new Promise((resolve,reject)=>{
	resolve('success');
}));
console.log(p3);

let p4 = Promise.resolve(Promise.resolve(Promise.resolve("OK")));
console.log(p4);

8.2 Promise.reject()

返回的结果始终为失败的Promise对象

console.log(Promise.reject(123));
console.log(Promise.reject(Promise.resolve('ok')));

8.3 Promise.catch()

功能是用来指定失败的回调函数

let p = new Promise((resolve,reject)=>{
	//resolve('success');
    reject('error');
});

p.catch(reason=>{
    console.log(reason);
});

//then方法中不是必须传入两个参数,可以只传递成功时的回调函数
//也可以单独使用catch来指定失败的回调函数

//异常(错误)穿透
//当如果有多个需要执行的成功时的回调函数,可以不需要每一次都写失败回调,可以统一最后利用catch
//当如果promise对象的状态为reject的话,会一直向下穿透直到catch方法
p.then(value=>{
    console.log(value);
}).then(value=>{
    console.log(value);
}).catch(reason=>{
    console.log(reason);
})

8.4 Promise.all()

作用:针对于多个Promise的异步任务进行处理

接收的参数:promise数组

返回值:promise对象,状态由promise数组中的对象状态决定

  • 若每个对象状态都为成功,则返回的promise对象状态为成功,

​ 成功的结果值为每个promise对象成功结构值组成的数组

  • 其中一个对象状态为失败,则返回的promise对象状态为失败,

​ 失败的结果值为失败的promise对象的结果值

let p1 = new Promise((resolve, reject) => {
            resolve('ok');
})
let p2 = Promise.resolve('hello');
let p3 = Promise.resolve('oh yeah');
let result = Promise.all([p1, p2, p3])
console.log(result);

当有一个ajax请求,它的参数需要另外2个甚至更多请求都有返回结果之后才能确定,

那么这个时候,就需要用到Promise.all来帮助我们应对这个场景。

Promise.all接收一个Promise对象组成的数组作为参数,

当这个数组所有的Promise对象状态都变成resolved或者rejected的时候,它才会去调用then方法。

//ES6中对Promise.all()的理解以及应用场景
//用于将多个Promise实例,包装成一个新的Promise实例
let p1 = new Promise((resolve,reject)=>{
   resolve('成功01');
})
let p2 = new Promise((resolve,reject)=>{
	resolve('成功02');
}).catch(reason=>console.log(reason));
let p3 = new Promise((resolve,reject)=>{
	resolve('成功03');
})
//参数可以不是数组,但必须是iterator接口
let pAll = Promise.all([p1,p2,p3]);
console.log(pAll)
//pAll的状态是由p1,p2,p3来决定,只有当这三个都为成功,pAll才会为成功,反之,但凡其中一个失败结果就是失败
//这个时候第一个失败的实力的返回值会传递给pAll的回调函数,如果作为参数的实例,自己定义了catch方法,那么它一旦为rejected,是不会触碰到pAll中的catch方法
pAll.then(value=>{
	console.log(value);
},reason=>{
	console.log(reason);
})

案例1:模拟请求三个接口中的数据,全部请求成功后获取。

function getUsersList() {
    return new Promise((resolve, reject) => {
        //模拟请求用户列表数据
        setTimeout(() => {
            resolve('用户列表的数据');
        }, 1000);
    })
}
function getBannersList() {
    return new Promise((resolve, reject) => {
        //模拟请求用户列表数据
        setTimeout(() => {
            resolve('轮播图的数据');
        }, 2000);
    })
}
function getVideoList() {
    return new Promise((resolve, reject) => {
        //模拟请求用户列表数据
        setTimeout(() => {
            resolve('视频列表的数据');
        }, 3000);
    })
}
//初始加载的时候
function initLoad() {
    let all = Promise.all([getUsersList(), getBannersList(), getVideoList()]);
    //获取成功请求的结果值
    all.then(value => {
        console.log(value);
    })
}
initLoad();

案例2:修改多文件读取代码

const fs = require('fs');
const util = require('util');
const mywriteFile = util.promisify(fs.readFile);
let one = mywriteFile('./resource/1.html');
let two = mywriteFile('./resource/2.html');
let three = mywriteFile('./resource/3.html');
let result = Promise.all([one,two,three]);
result.then(value=>{
    console.log(value.join(''));
},reason=>{
    console.log(reason);
})

8.5 Promise.race()

Promise.race race 赛跑的意思

参数: promise 数组

返回结果: promise 对象

状态由『最先改变状态的 promise对象』决定

结果值由 『最先改变状态的 promise对象』决定

let p1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('ok');
            }, 2000)
});
let p2 = Promise.resolve('success');
let p3 = Promise.resolve('oh hou');
let result = Promise.race([p1, p2, p3]);
console.log(result);

与Promise.all相似的是,Promise.race都是以一个Promise对象组成的数组作为参数。

不同的是,只要当数组中的其中一个Promsie状态变成resolved或者rejected时,就可以调用.then方法了。

而传递给then方法的值也会有所不同。

<script>
    //ES6中Promise.race的用法以及使用场景
    //将多个Promise实例包装成一个新的Promise实例
    let p1 = new Promise((resolve, rejct) => {
        setTimeout(() => {
            resolve('p1成功')
        }, 2000);
    })
    let p2 = new Promise((resolve, rejct) => {
        setTimeout(() => {
            resolve('p2成功');
        }, 1000);
    }, 1000);
    //调用
    const prace = Promise.race([p1, p2]);
    //Promise.race区别于Promise.all:
    //只要是实例中有一个先改变状态,就会把这个实例的返回值传递给prace的回调函数
</script>
//使用场景:请求超时提示
function request() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('请求成功');
        }, 4000);
    })
}
function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('网络不畅,请求超时');
        }, 3000);
    });
}
Promise.race([request(), timeout()]).then(value => {
    console.log(value)
}).catch(reason => {
    console.log(reason)
})

8.6 Promise.allSettled()

Promise.allSettled()方法,用来确定要一组异步操作是否都结束了(不管成功或失败)。

所以,它的名字叫"Settled",包含了"fufilled"和"rejected"两种情况.

<script>
    function ajax(url) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.open('get', url, true);
        xhr.send();
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    resolve(xhr.responseText);
                } else {
                    reject(xhr.responseText);
                }
            }
        }
    })

}
//类比Promise下的all方法和allSettled
// Promise.all([ajax('http://www.xiongmaoyouxuan.com/api/tabs'),
// ajax('https://m.maizuo.com/gateway?cityId=110100&k=4770248')
// ]).then(value => {
//     console.log(value)
// }).catch(error => {
//     console.log(error);
// })

Promise.allSettled([ajax('http://www.xiongmaoyouxuan.com/api/tabs'),
                    ajax('https://m.maizuo.com/gateway?cityId=110100&k=4770248')
                   ]).then(value => {
    // console.log(value)
    let successList = value.filter(item => item.status === 'fulfilled');
    console.log(successList)

    let errorList = value.filter(item => item.status === 'rejected');
    console.log(errorList)
}).catch(error => {
    console.log(error);
})
</script>

8.7 Promise.any()

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfiilled状态;

如果所有参数实例都变成rejected,包装实例就会变成rejected状态。

Promise.any()跟Promise.race()方法很像,但是有一点不同,
就是Promise.any()不会因为某个Promise变成rejected状态而结束,
必须等到所有参数Promise变成rejected状态才会结束。

<script>
    let p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('ok');
        }, 1000)
    })
    let p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('okk');
        }, 2000)
    })
    let p3 = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('error');
        }, 3000)
    })
    Promise.any([p1, p2, p3]).then(res => {
        console.log(res)
    }).catch(err => {
        console.log('error')
    })
</script>

8.8 Promise.finally()

finally是在ES9(ES2018)中新增的一个特性:表示无论Promise对象变成fufilled还是rejected状态,最终都会被执行。

finally方法中的回调函数是不接受参数的,因为无论前面是fulfilled状态还是rejected状态, 它都是执行。

const p = new Promise((resolve, reject) => {
    // resolve('ok');
    reject('error');
});
p.then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
}).finally(() => {
    console.log('finally')
})

你可能感兴趣的:(javascript,开发语言,ecmascript)