前言:
在 Promise 中,异步操作的结果有三种状态:等待(pending)、已完成(fulfilled)和已拒绝(rejected)。当异步操作完成后,Promise 对象会从等待状态转变为已完成或已拒绝状态。如果异步操作成功完成,则 Promise 对象处于已完成状态,并携带着异步操作的返回值;如果异步操作发生错误,则 Promise 对象处于已拒绝状态,并携带着一个错误信息。
Promise 可以通过链式调用来实现多个异步操作依次执行的效果,这种方式比传统的回调函数更加简洁、灵活和易读。
目录
简介
1.抽象表达:
(1) Promise 是一门新的技术(ES6 规范)
(2) Promise 是JS 中进行异步编程的新解决方案
备注: 旧方案是单纯使用回调函数
2. 具体表达
(1) 从语法上来说: Promise 是一个构造函数
(2) 从功能上来说: promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值
异步编程
- fs·文件操作 require('fs').readFile('./index.html',(err,data)=>小)
- 数据库操作
- AJAX $.get('/server'(data)=>)
- 定时器 setTimeout(( )=>{},2000);
3.特点:
①指定回调函数的方式更加灵活
1. 旧的: 必须在启动异步任务前指定
2.promise: 启动异步任务 => 返 promie对象 =>给 promise 对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
②支持链式调用,可以解决回调地狱问题
1. 什么是回调地狱?
回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件。
2.回调地狱的缺点?
- 不便于阅读
- 不便于异常处理
3.解决方案?
promise 链式调用。
案例
Promise 形式实现 都是函数类型的数据
resolve 解决
reject 拒绝
//点击按钮,2 后显示是否中奖(30%概率中奖)
//若中奖弹出恭喜恭喜,奖品为 19万 RMB 劳斯莱斯优惠券。若未中奖弹出再接再厉
//生成随机数
function rand(m,n){
return Math.ceil(Math.random() * (n-m+1)) + m-1;
//Math.ceil()方法返回一个大于或等于数字的最小整数,即向上取整
}
//获取元素对象
const btn = document.querySelector('#btn');
btn.addEventListener('click', function(){
const p = new Promise((resolve, reject) => [
setTimeout(() => [
//30% 1-100 1 2 30
//获取从1 - 1的一个随机数
let n = rand(1, 100);
//判断
if(n <= 30){
resolve(n); // 将 promise 对象的状态设置为 [成功J}elsef
}else{
reject(n); // 将 promise 对象的状态设置为 [失败]
}
}, 1000);
});
//调用 then 方法 value 值 reason 理由
p.then((value) => {
alert('恭喜恭喜,奖品为 1万 RMB 劳斯莱斯优惠券,中奖号码为'+value);
}, (reason) => {alert("再接再厉,号码为'+reason);
}),
})
fs读取文件
const fs = require( 'fs')
回调函数 形式
fs.readFile('./resource/content.txt', (err, data) => {
// 如果出错 则抛出错误
if(err) throw err;
//输出文件内容
console.log(data.toString());
});
fs是node.js里面的一个模块,叫文件系统模块。
//Promise 形式
let p = new Promise((resolve,reject) =>
fs .readFile('./resource/content.txt',(err, data) => {
//如果出错
if(err) reject(err);
//如果成功
resolve(data);
});
});
//调用 then
p.then(value=>{
console.log(value.tostring());
},reason =>(
console.log(reason);
})
Promise封装fs读取文件操作
封装一个函数 mineReadFile 读取文件内容
参数: path 文件路径
返回: promise 对象
function mineReadFile(path){
return new Promise((resolve, reject) => {
//读取文件
require('fs').readFile(path, (err, data) =>{
//判断
if(err) reject(err);
//成功
resolve();
});
});
}
mineReadFile('./resource/content.txt')
.then(value=>{
//输出文件内容
console.log(value.tostring());
}reason=>[
console.log(reason);
});
promisify方法进行promise风格转化
传入一个遵循常见的错误优先的回调风格的函数
(即以 (err,value) =>... 回调作为最后一个参数),并返回一个返回 promise 的版本。
//引入 util 模块
const util = require('util');//引入 fs 模块
const fs = require('fs');
//返回一个新的函数
let mineReadFile = util.promisify(fs.readFile);
mineReadFile('./resource/content.txt').then(value=>{
console.log(value.tostring());
});
封装ajax请求
封装一个函数 send AJAX 发送 GET AJAX 请求
参数URL
返回结果 Promise 对象
function sendAJAX(url){
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.responseType ='json';
xhr.open("GET",ur1);
xhr.send();
//处理结果
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
//判断成功
if(xhr.status >= 20 && xhr.status < 300){
//成功的结果
resolve(xhr.response);
}else{
reject(xhr.status);
}
}
}
});
sendAJAX('https://api.apiopen.top/getJoke')
.then(value => {
console.log(value);
}, reason => {
console.warn(reason);
});
promise的状态改变
pending 变为 resolved
pending 变为 rejected
说明: 只有这 2 种,且一个 promise 对象只能改变一次。
无论变为成功还是失败,都会有一个结果数据
成功的结果数据一般称为 value,失败的结果数据一般称为 reason
Promise 的状态
实例对象中的一个属性
①PromiseState
pending 未决定的
resolved / fullfilled 成功
rejected 失败
②Promise 对象的值
实例对象中的另一个属性 PromiseResult
保存着对象[成功/失败]的结果
resolve
reject
Promise工作流程
promise API
1. Promise 构造函数: Promise (excutor){ }
(1) executor 函数: 执行器(resolve, reject) => { }
(2) resolve 函数: 内部定义成功时我们调用的函数 value =>{ }
(3) reject 函数: 内部定义失败时我们调用的函数 reason =>{ }
说明: executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行
2.Promise.prototype.then 方法: (onResolved, onRejected) => { }
(1) onResolved,函数: 成功的回调函数 (value) =>
(2) onRejected 函数: 失败的回调函数 (reason) =>说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调返回一个新的 promise 对象
3. Promise.prototype.catch 方法:(onRejected) => { }
(1)onRejected 函数: 失败的回调函数 (reason) =>{ }
说明: then( )的语法糖,相当于:then(undefined,onRejected)
4. Promise.resolve 方法: (value) =>{ }
(1) value: 成功的数据 promise 对象
说明: 返回一个成功/失败的 promise 对象
//如果传入的参数为 非Promise类型的对象,则返回的结果为成功promise对象
//如果传入的参数为 Promise 对象,则参数的结果决定了 resolve 的结果
5.Promise.reject 方法:(reason) =>{ }
(1)reason: 失败的原因
说明: 返回一个失败的 promise 对象
6.Promise.all 方法:(promises) => { }
(1) promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败
7.Promise.race 方法:(promises) =>{ }
(1) promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态
(1)Promise 构造函数: Promise(excutor) { }
excutor,函数: 同步执行 (resolve,reject) => { }
resolve 函数: 内部定义成功时我们调用的函数 value => { }
reject 函数:内部定义失败时我们调用的函数 reason => { }
说明: excutor 会在 romise 内部立即同步回调,异步操作在执行器中执行
(2)Promise.prototype.then 方法: (onResolved,onRejected) =>{onResolved 函数:成功的回调函数 (value) => { }
关键问题
如何修改对象的状态
let p = new Promise((resolve, reject) =>{
//1.resolve 函数
// resolve( 'ok'); // pending=> fulfilled (resolved)
//2.reject 函数
// reject("error");// pending=> rejected
//3。抛出错误
throw'出问题了';
});
console.log(p);
一个 promise 指定多个成功/失败回调函数,都会调用吗?
当 promise 改变为对应状态时都会调用。
改变 promise 状态和指定回调函数谁先谁后?
(1)都有可能,正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调。(执行器函数是同步执行的,一般来说会先改变状态。如果resolve写在定时器里,则then先执行)
(2)如何先改状态再指定回调?
- 在执行器中直接调用 resolve( )/reject( )
- 延迟更长时间才调用 then( )
(3) 什么时候才能得到数据?
①如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
②如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
promise.then( )返回的新 promise 的结果状态由什么决定?
(1) 简单表达: 由 then( )指定的回调函数执行的结果决定
(2) 详细表达:
①如果抛出异常,新 promise 变为 reiected,reason 为抛出的异常
②如果返回的是非 promise 的任意值,新 promise变为 resolved, value 为返回的值
③如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果
即默认result的状态为成功fulfilled的,即使是p.then调用了reject回调函数
如果p.then里面的回调函数有return,并且return了一个Promise对象
那么此时的resut的状态和由returh返的Pomise对象里的状态和值决定。
promise 如何串连多个操作任务?
(1) promise 的 then( )返回一个新的 promise可以开成 then( )的链式调用
(2)通过 then 的链式调用串连多个同步/异步任务
.then的确返回promise对象,但这个对象的值由回调返回的值决定,这里没有声明返回值,所以返回undefined,那么下一个then获取到的值就是undefined,直接打印出来。
promise 异常传透?
(1)当使用 promise 的 then 链式调用时,可以在最后指定失败的回调
(2) 前面任何操作出了异常,都会传到最后失败的回调中处理 catch( )
返回pending的promise,状态没有改变,then方法不可以调用
中断 promise 链?
(1) 当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的回调函数
(2) 办法: 在回调函数中返回一个 pendding,状态的 promise 对象
自定义(手写)Promise
function (window) {
//Promise 构造函数
//内部同步执行的函数excutor:(resolve,reject) => {
function Promise(excutor) {
}
})
function Promise(executor){
//resolve函数
function resolve(data){
}
//reject函数
function reject(data){
}
//同步调用[执行器函数]
executor(resolve,reject);
}
Promise.prototype.then = function(onResolved, onRejected){ }