Promise入门_1
1. 预备知识
1.1 实例对象与函数对象
- 实例对象:new 函数产生的对象,称为实例对象,简称为对象
- 函数对象:将函数作为对象使用时,称为函数对象
function Fn() { // Fn只能称为函数
}
const fn = new Fn() // Fn只有new过的才可以称为构造函数
//fn称为实例对象
console.log(Fn.prototype)// Fn作为对象使用时,才可以称为函数对象
Fn.bind({}) //Fn作为函数对象使用
$('#test') // $作为函数使用
$.get('/test') // $作为函数对象使用
()左边是函数,点(。)左边是对象(函数对象、实例对象)
1.2 两种类型的回调函数
1. 同步回调
立即执行,完全执行完了才结束,不会放入回调队列中。
数组遍历相关的回调 / Promise的executor函数
const arr = [1, 3, 5];
arr.forEach(item => { // 遍历回调,同步回调,不会放入队列,一上来就要执行
console.log(item);
})
console.log('forEach()之后')
2. 异步回调
不会立即执行,会放入回调队列中将来执行。
定时器回调 / ajax回调 / Promise成功或失败的回调。
// 定时器回调
setTimeout(() => { // 异步回调,会放入队列中将来执行
console.log('timeout callback()')
}, 0)
console.log('setTimeout()之后')
// Promise 成功或失败的回调
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {console.log('value', value)},
reason => {console.log('reason', reason)}
)
console.log('----')
// ----
// value 1
js 引擎先把初始化的同步代码都执行完成后,才执行回调队列中的代码
1.3 JS中的异常error处理
1. 错误的类型
Error:所有错误的父类型。
ReferenceError:引用的变量不存在。
console.log(a) // ReferenceError:a is not defined
TypeError:数据类型不正确。
let b
console.log(b.xxx)
// TypeError:Cannot read property 'xxx' of undefined
let c = {}
c.xxx()
// TypeError:c.xxx is not a function
RangeError:数据值不在其所允许的范围内。
function fn() {
fn()
}
fn()
// RangeError:Maximum call stack size exceeded
SyntaxError:语法错误
const c = """"
// SyntaxError:Unexpected string
2. 错误处理(捕获与抛出)
抛出错误:throw error
function something() {
if (Date.now()%2===1) {
console.log('当前时间为奇数,可以执行任务')
} else { //如果时间为偶数抛出异常,由调用来处理
throw new Error('当前时间为偶数,无法执行任务')
}
}
捕获错误:try ... catch
// 捕获处理异常
try {
something()
} catch (error) {
alert(error.message)
}
3. 错误对象
massage 属性:错误相关信息
stack 属性:函数调用栈记录信息
try {
let d
console.log(d.xxx)
} catch (error) {
console.log(error.message)
console.log(error.stack)
}
console.log('出错之后')
// Cannot read property 'xxx' of undefined
// TypeError:Cannot read property 'xxx' of undefined
// 出错之后
因为错误被捕获处理了,后面的代码才能运行下去,打印出‘出错之后’
2. Promise的理解和使用
2.1 Promise是什么
1. 理解Promise
- 抽象表达:Promise是JS中进行异步编程的新的解决方案(旧方案是单纯使用回调函数)
---- 异步编程 ①fs 文件操作 ②数据库操作 ③Ajax ④定时器
【推荐阅读 【JavaScript】同步与异步-异步与并行-异步运行机制-为什么要异步编程-异步与回调-回调地狱-JavaScript中的异步操作】
- 具体表达:
①从语法上看:Promise是一个构造函数 (自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法)
②从功能上看:promise对象用来封装一个异步操作并可以获取其成功/失败的结果值。
- 阮一峰的解释:
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
2. Promise 的状态
实例对象promise中的一个属性 PromiseState
Promise 的状态:pending:未决定的、resolved:成功、rejected:失败。
Promise 的状态改变只有如下两种:
-
pending 变为 resolved/fullfilled
-
pending 变为 rejected
注意:
- throw也会改变promise的状态。
- 对象的状态不受外界影响。
- 只有这两种,且一个 promise 对象的状态只能改变一次。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
- 无论成功还是失败,都会有一个结果数据。成功的结果数据一般称为 value,而失败的一般称为 reason。
3. Promise对象的值
实例对象promise的另一个值 PromiseResult
它是实例对象promise的一个属性。
保存着对象异步任务的结果: 成功/失败 的值(value/reason
)
resolve/reject
这两个函数可以修改PromiseResult属性的值
4. Promise 的基本流程
5. Promise 的基本使用
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(reason);
}
});
Promise
构造函数接受一个函数(执行器函数)作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
resolve
函数的作用是,将Promise
对象的状态从“未完成”变为“成功”(即从 pending
变为 resolved
),在异步操作成功时调用,并将异步操作的结果,作为参数value
传递出去;
reject
函数的作用是,将Promise
对象的状态从“未完成”变为“失败”(即从 pending
变为 rejected
),在异步操作失败时调用,并将异步操作报出的错误,作为参数error/reason
传递出去。
Promise
实例生成以后,可以用then方法分别指定resolved
状态和rejected
状态的回调函数。
promise.then(function(value) {
// success
}, function(reason) {
// failure
});
then
方法可以接受两个回调函数作为参数。
第一个回调函数onResolved()
是Promise
对象的状态变为resolved
时调用
第二个回调函数onRejected()
是Promise
对象的状态变为rejected
时调用
这两个函数都是可选的,不一定要提供。它们都接受Promise
对象传出的值作为参数。
- 一个例子
// 创建一个新的p对象promise
const p = new Promise((resolve, reject) => { // 执行器函数
// 执行异步操作任务
setTimeout(() => {
const time = Date.now()
// 如果当前时间是偶数代表成功,否则失败
if (time % 2 == 0) {
// 如果成功,调用resolve(value)
resolve('成功的数据,time=' + time)
} else {
// 如果失败,调用reject(reason)
reject('失败的数据,time=' + time)
}
}, 1000);
})
p.then(
value => { // 接收得到成功的value数据 onResolved
console.log('成功的回调', value) // 成功的回调 成功的数据,time=1615015043258
},
reason => { // 接收得到失败的reason数据 onRejected
console.log('失败的回调', reason) // 失败的回调 失败的数据,time=1615014995315
}
)
.then() 和执行器(executor)同步执行,.then() 中的回调函数异步执行。
执行器就是Promise构造函数的那个匿名函数参数。
2.2 为什么要用 Promise
1.指定回调函数的方式更加灵活
旧的:必须在启动异步任务前指定,如:
// 1. 纯回调的形式
// 成功的回调函数
function successCallback(result) {
console.log("声音文件创建成功:" + result);
}
// 失败的回调函数
function failureCallback(error) {
console.log("声音文件创建失败:" + error);
}
// 必须先指定回调函数,再执行异步任务
createAudioFileAsync(audioSettings, successCallback, failureCallback) // 回调函数在执行异步任务(函数)前就要指定
promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定)
// 2. 使用Promise
const promise = createAudioFileAsync(audioSettings); // 执行2秒
setTimeout(() => {
promise.then(successCallback, failureCallback) // 也可以获取
}, 3000);
2.支持链式调用,可以解决回调地狱问题
什么是回调地狱?
回调函数嵌套调用,外部回调函数异步执行的结果是其内部嵌套的回调函数执行的条件。如:
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result:' + finalResult)
}, failureCallback)
}, failureCallback)
}, failureCallback)
回调地狱的缺点?
不便于阅读,不便于异常处理。
解决方案?
promise 链式调用。promise是一个构造函数,他本身是同步执行的,只不过他身上的then()方法是异步执行的。promise和ajax没有必然联系,它只是用来封装异步操作而已
使用 promise 的链式调用解决回调地狱:
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {console.log('Got the final result:' + finalResult)})
.catch(failureCallback)
终极解决方案?
async/await
回调地狱的终极解决方案 async/await
async function request() {
try{
const result = await doSomething()
const newResult = await doSomethingElse(result)
const finalResult = await doThirdThing(newResult)
console.log('Got the final result:' + finalResult)
} catch (error) {
failureCallback(error)
}
}
2.3 如何使用 Promise
1. Promise 构造函数:Promise(executor) {}
executor
函数:同步执行(resolve, reject) => {}
resolve
函数:内部定义成功时调用的函数resove(value)
reject
函数:内部定义失败时调用的函数reject(reason)
说明:executor
是执行器,会在 Promise
内部立即同步回调,异步操作 resolve/reject
就在 executor
中执行。执行器指的就是Promise的那个带两个形参的匿名函数。
如:
let p=new Promise((resolve,reject)=>{
//一般来说回调函数是执行完所有代码后执行的(异步),这里111在222前面输出证明是同步
console.log(222);
resolve();//改变promise实例的值。调用的then是异步的。
});
p.then(value=>{
//promise里面的函数是同步,它的then方法是异步
//promise对象的then方法是异步执行的,也就是属于回调代码,是等初始化代码执行完后才执行的。
console.log(333);
});
console.log(111);
输出顺序如下:
2. Promise.prototype.then 方法:p.then(onResolved, onRejected)
指定两个回调(成功+失败)
onResolved
函数:成功的回调函数 (value) => {}
onRejected
函数:失败的回调函数 (reason) => {}
说明:指定用于得到成功 value
的成功回调和用于得到失败 reason
的失败回调,返回一个新的 promise
对象
练习1:点击按钮, 1s 后显示是否中奖(30%概率中奖)
若中奖弹出 恭喜恭喜, 奖品为 10万 RMB 劳斯莱斯优惠券
若未中奖弹出 再接再厉
//生产范围的随机数函数
function rand(x,y){
return Math.round(Math.random()*(y-x)+x);
}
//获取按钮
let btn=document.querySelectorAll("button")[0];
btn.addEventListener("click",function(){ //绑定事件
//Promise形式实现:
//resolve:匿名函数参数,是一个函数,表示解决,成功的时候调用。
//reject:匿名函数参数,是一个函数,表示拒绝,失败的时候调用。
let p=new Promise((resolve,reject)=>{
setTimeout(() => {
let number=rand(1,100);
if(number<=30){
resolve();//该函数调用之后会将Promise的实例的状态设置为 “成功”
}else{
reject();//该函数调用之后会将Promise的实例的状态设置为 “失败”
}
}, 1000);
});
//调用then方法,第一个匿名函数是对象状态为成功时的回调,第二个是对象状态为失败时候的回调。
p.then(()=>{
alert("你中奖了")
},()=>{
alert("再接再厉");
});
});
练习2:将练习1中提示框里输出用户的中奖号码,借助resolve和reject两个函数的参数进行传递给它们的回调函数。
只需修改如下代码:
btn.addEventListener("click",function(){ //绑定事件
let p=new Promise((resolve,reject)=>{
setTimeout(() => {
let number=rand(1,100);
if(number<=30){
resolve(number)
}else{
reject(number);
}
}, 2000);
});
p.then((value)=>{
alert("你中奖了,你的中奖数字为:"+value)
},(reason)=>{
alert("再接再厉,你的号码为:"+reason);
});
});
练习3:用promise封装fs文件操作
const fs=require("fs");
let p=new Promise((resolve,reject)=>{
fs.readFile("./resource/content.txt",function(err,data){
//判断是否成功
if(err) reject(err);
resolve(data);
});
});
p.then(value=>console.log(value.toString()),reason=>console.log(reason));
练习4:用promise封装ajax请求操作:
原生写法:
封装写法:
使用util.promisify方法将要传回调函数的方法进行promise风格转化。
也就是需要用回调函数的可以同promise的then方法来传回调函数。
promise封装发送ajax请求
function sendAjax(url){
let p=new Promise((resolve,reject)=>{
let xhr=new XMLHttpRequest();
xhr.open("GET",url);
xhr.send();
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
if(xhr.status>=200&& xhr.status<300){
resolve(xhr.response);
}else{
reject(xhr.status);
}
}
}
});
//返回这个promise的实例对象
return p;
}
调用这个返回会返回promise对象的实例,可以通过实例调用then方法设置回调函数。
3. Promise.prototype.catch 方法:p.catch(onRejected)
指定失败的回调
1)onRejected
函数:失败的回调函数 (reason) => {}
说明:这是then()
的语法糖,相当于 then(undefined, onRejected)
new Promise((resolve, reject) => { // excutor执行器函数
setTimeout(() => {
if(...) {
resolve('成功的数据') // resolve()函数
} else {
reject('失败的数据') //reject()函数
}
}, 1000)
}).then(
value => { // onResolved()函数
console.log(value) // 成功的数据
}
).catch(
reason => { // onRejected()函数
console.log(reason) // 失败的数据
}
)
4. Promise.resolve 方法:Promise.resolve(value)
value
:将被 Promise
对象解析的参数,也可以是一个成功或失败的 Promise
对象
返回:返回一个带着给定值解析过的 Promise 对象,如果参数本身就是一个 Promise 对象,则直接返回这个 Promise 对象。
- 如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象
let p1 = Promise.resolve(521);
console.log(p1); // Promise {<fulfilled>: 521}
- 如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
// resolve('OK'); // 成功的Promise
reject('Error');
}));
console.log(p2);
p2.catch(reason => {
console.log(reason);
})
- 如果传入的参数也是一个promise实例,那么这个实例的值就决定了resove返回的promise对象的值,即实例的值是成功,那就返回的对象的值也是成功,反之也一样。
5. Promise.reject 方法:Promise.resolve(reason)
reason:失败的原因
说明:返回一个失败的 promise
对象
let p = Promise.reject(521);
let p2 = Promise.reject('iloveyou');
let p3 = Promise.reject(new Promise((resolve, reject) => {
resolve('OK');
}));
console.log(p);
console.log(p2);
console.log(p3);
- Promise.resolve()/Promise.reject() 方法就是一个语法糖
- 用来快速得到
Promise
对象
//产生一个成功值为1的promise对象
new Promise((resolve, reject) => {
resolve(1)
})
//相当于
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
p1.then(value => {console.log(value)}) // 1
p2.then(value => {console.log(value)}) // 2
p3.catch(reason => {console.log(reason)}) // 3
6. Promise.all 方法:Promise.all(iterable)
iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String
说明:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败。
全部成功返回成功的值,值里面有每个成功的promise对象的值的数组。
let p1 = new Promise((resolve, reject) => {
resolve('OK');
})
let p2 = Promise.reject('Error');
let p3 = Promise.resolve('Oh Yeah');
const result = Promise.all([p1, p2, p3]);
console.log(result);
7. Promise.race方法:Promise.race(iterable)
iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String
说明:返回一个新的 promise
,第一个完成的 promise
的结果状态就是最终的结果状态
谁先完成就输出谁(不管是成功还是失败)
使用场景:ajax请求和超时回调,ajax在超时回调执行前完成,显示回调结果,否则显示请求超时
const pRace = Promise.race([p1, p2, p3])
// 谁先完成就输出谁(不管是成功还是失败)
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
pRace.then(
value => {
console.log('race onResolved()', value)
},
reason => {
console.log('race onRejected()', reason)
}
)
//race onResolved() 2
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');
//调用
const result = Promise.race([p1, p2, p3]);
console.log(result);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构