一、什么叫回调函数?
回调函数:被作为参数传给另外一个函数;
二、常见的内置错误
- ReferenceError:引用的变量不存在
- TypeError:数据类型不正确
- RangeError:数据值不在其允许的范围内
- SyntaxError:语法错误
三、错误的处理
- 捕获错误
try {
let b = null
console.log(b.null)
} catch (err) {
// err是一个对象,该对象中有两个属性
// message(错误相关信息)和stack(函数调用栈记录信息)
console.log(err.message)
// Cannot read property 'null' of null
}
- 抛出错误
function something() {
if (Date.now() % 2 === 1) {
console.log("当前时间为奇数,可以执行任务")
} else { // 抛出异常,由调用来处理
throw new Error("当前时间为偶数,无法执行任务")
}
}
// 捕获处理异常
try {
something()
} catch (error) {
console.log(error.message)
}
四、为什么要用promise?
语法角度:Promise是一个构造函数
功能角度:Promise对象用来封装一个异步操作并可以获取结果;
五、Promise状态改变
1)pending变为resolved
2)pending变为rejected
只有这两种状态改变,且promise对象只能改变一次状态;无论成功还是失败,都会有一个结果数据;
成功的结果数据称为value,失败的结果数据称为reason
resolve(1) // promise由pending状态变为resolved成功状态
reject(2) // promise由pending状态变为rejected失败状态
throw new Error("出错了")
// 抛出异常,promise变为rejected状态,reason值为抛出的error
throw 3
// 抛出异常,promise变为rejected失败状态,reason为抛出的3
六、Promise基本运行流程
- 创建一个Promise对象
new Promise()需要传递一个参数
这个参数是回调函数(执行器函数)
- new Promise执行异步操作:
- 成功的话,执行resolve函数——promise对象状态变为resolved状态——回调onResolved()函数(使用.then())——返回新的promise对象
- 失败的话,执行reject函数——promise对象状态变为rejected状态——回调onRejected()函数(使用.then()或者.catch())——返回新的promise对象
<script type="text/javascript">
// 1.创建一个promise对象
const p = new Promise((resolve, reject) => {
// 执行器函数 同步回调
console.log("执行excutor")
// 2.执行异步操作任务
setTimeout(() => {
const time = Date.now()
// 3.1. 如果成功了,调用resolve(value)
if (time % 2 == 0) {
resolve("成功的数据,time=" + time)
} else {
// 3.2. 如果失败了,调用reject(reason)
reject("失败的数据,time=" + time)
}
}, 1000)
})
console.log("new Promise()之后")
p.then(
value => { // 接收得到成功的value数据 onResolved
console.log("成功的回调", value)
},
reason => { // 接收得到失败的reason数据 onRejected
console.log("失败的回调", reason)
}
)
</script>
// 执行excutor
// new Promise()之后
// 成功的回调 成功的数据,time=1618713417808
七、为什么要用Promise函数?
// 成功的回调函数
function successCallback(result) {
console.log("声音文件创建成功:" + result)
}
// 失败的回调函数
function failureCallback(error) {
console.log("声音文件创建失败:" + error)
}
// 1.1 使用纯回调函数
createAudioFileAsync(audioSetting, successCallback,
failureCallback)
// 1.2 使用Promise
const promise = createAudioFileAsync(audioSetting);
setTimeout(() => {
promise.then(successCallback, failureCallback)
}, 1000)
- 支持链式调用,解决回调地狱问题;回调地狱:回调函数的嵌套使用,不便于阅读(多个串联的异步操作,第一个执行完再执行第二个);
// 2.1 回调地狱
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log("Got the final result" + finalResult)
}, failureCallback)
}, failureCallback)
}, failureCallback)
// 2.2 使用promise的链式调用解决回调函数
// 编码从上往下写,不需要嵌套
// 异常传透:当任何一个执行出了问题,都会去执行catch()
doSomething().then(function(result) {
return doSomethingElse(result)
}).then(function(newResult) {
return doThirdThing(newResult)
}).then(function(finalResult) {
console.log("Got the final result" + finalResult)
}).catch(failureCallback)
// 2.3 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 (err) {
failureCallback(error)
}
}
- 指定回调函数的方式更加灵活:可以先指定回调函数再改变状态,也可以先改变状态再指定回调函数;但是之前的方法只能先指定回调函数(一般情况下,先指定回调函数,再改变状态,使用setTimeout)
promise:启动异步任务——返回promise对象
——给promise对象绑定回调函数
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1) // 后改变状态(同时指定数据),异步执行回调函数
}, 1000)
}).then( // 先指定回调函数,先保存当前指定的回调函数
value => {console.log(value)}, // 1
reason => {console.log("reason", reason)}
)
new Promise((resolve, reject) => {
resolve(1) // 先改变状态(同时指定数据)
}).then( // 后指定回调函数,异步执行回调函数
value => {console.log(value)}, // 1
reason => {console.log("reason", reason)}
)
八、Promise的API
Promise.all() all是函数对象的方法;
Promise.prototype.then() 所有的实例对象都可以调用then()方法,then是原型对象的方法;
1. Promise构造函数:Promise(excutor) {}
excutor函数:同步执行(resolve, reject) => {}
resolve函数:内部定义成功时,立刻调用函数 value => {}
reject函数:内部定义失败时,立刻调用函数 reason => {}
说明:excutor会在Promise内部立即同步回调,异步操作在执行器中执行
2.Promise.prototype.then方法:(onResolved, onRejected)
onResolved函数:成功的回调函数(value) => {}
onRejected函数:失败的回调函数(reason) => {}
说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调,
会返回一个新的promise对象
3.Promise.prototype.catch方法:(onRejected) => {}
onRejected函数:失败的回调函数 (reason) => {}
说明:then()的语法糖,相当于:then(undefined, onRejected)
4.Promise.resolve方法:(value) => {}
value:成功的数据或者promise对象
说明:返回一个成功/失败的promise对象
5.Promise.reject方法:(reason) => {}
reason:失败的原因
说明:返回一个失败的promise对象
6.Promise.all方法:(promises) => {}
promises:包含n个promise的数组
说明:返回一个新的promise,只有所有的promise都成功才成功,
只要有一个失败就直接失败
7.Promise.race方法:(promises) => {}
promises:包含n个promise的数组
说明:返回一个新的promise,第一个完成的promise的结果状态
就是最终的结果状态;
Promise.all()
let wake = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${time / 1000}秒后醒来`)
}, time)
})
}
let p1 = wake(3000)
let p2 = wake(2000)
Promise.all([p1, p2]).then((result) => {
console.log(result) // [ '3秒后醒来', '2秒后醒来' ]
}).catch((error) => {
console.log(error)
})
九、promise.then()返回的新的promise对象的状态由什么决定
- 简单表达:由then()指定的回调函数执行的结果决定;
- 详细表达:
- 如果抛出异常,新promise状态变为rejected,reason为抛出的异常;
- 如果返回的是非promise的任意值,则新promise的状态变为resolved,value为返回的值(没有return的话,value为undefined,否则,为return 后面的值)
- 如果返回一个新的promise对象,则此promise的状态由新promise对象的状态决定
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log("onResolved1()", value);
return Promise.reject(3)
},
reason => {console.log("onRejected1()", reason)}
).then(
value => {console.log("onResolved2()", value)},
reason => {console.log("onRejected2()", reason)}
)
十、Promise串联多个操作任务
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("执行任务1(异步)")
resolve(1)
}, 1000)
}).then(
value => {
console.log("任务1的结果:", value);
console.log("执行任务2(同步)");
return 2
}
).then(
value => {
console.log("任务2的结果:", value)
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("执行任务3(异步)")
resolve(3)
}, 1000)
})
}
).then(value => {
console.log("任务3的结果:", value)
})
// 执行任务1(异步)
// 任务1的结果: 1
// 执行任务2(同步)
// 任务2的结果: 2
// 执行任务3(异步)
// 任务3的结果: 3
十一、Promsie异常传透
-
使用promise的then链式调用时,可以在最后指定失败的回调;
-
前面任何操作出了异常,都会传到最后失败的回调中处理;(不是一下到了最后的失败回调函数,而是逐层传下的)
十二、中断Promise链
- 当使用promise的then()链式调用时,在中间中断,不再调用后面的回调函数
- 办法:在回调函数中返回一个pending状态的promise对象
return new Promise(() => {})
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log("onResolved1()", value);
return 2
}
).then(
value => {
console.log("onResolved2()", value)
return new Promise(() => {})
// 返回一个pending状态的Promise
}
).then(value => {
console.log("onResolved3()", value)
}).catch(reason => {
console.log("onRejected()", reason)
})
十三、async / await
async确保函数返回一个promise,如果代码中return <非promise> ,则JS会自动把返回的value包装成promise中resolve的参数;
async function f() {
return 2
}
f().then(value => {
console.log(value) // 2
})
// await等待当前promise执行完,才能往下执行其他的JS代码
async function f() {
return await new Promise((resolve, reject) => {
resolve(3)
reject(5)
})
}
f().then(value => {
console.log(value) // 3
})
使用async / await
let fs = require("fs")
async function f(filePath) {
return await new Promise((resolve, reject) => {
fs.readFile(filePath, "utf-8", (err, data) => {
if (err) {
reject(err)
}
resolve(data)
})
})
}
f("C:/Users/Jayson柴/Desktop/11.txt").then(res => {
console.log(res)
return f("C:/Users/Jayson柴/Desktop/22.txt")
}).then(res => {
console.log(res)
return f("C:/Users/Jayson柴/Desktop/33.txt")
}).then(res => {
console.log(res)
})
// 11 22 33
使用promise
let fs = require("fs")
function f(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, "utf-8", (err, data) => {
if (err) {
reject(err)
}
resolve(data)
})
})
}
f("C:/Users/Jayson柴/Desktop/11.txt").then(res => {
console.log(res)
return f("C:/Users/Jayson柴/Desktop/22.txt")
}).then(res => {
console.log(res)
return f("C:/Users/Jayson柴/Desktop/33.txt")
}).then(res => {
console.log(res)
})
// 11 22 33
十四、自定义Promise
// 自定义Promsie函数模块
(function(window) {
// Promise构造函数
// excutor:执行器函数(同步执行)
function Promise(excutor) {
// 把当前promise对象存起来
const self = this
self.status = "pending"
self.data = undefined
self.callbacks = [] // 元素的结构:{onResolved() {}, onRejected() {}}
function resolve(value) {
// 如果当前状态不是pending,直接结束
// 意思就是如果前面执行过resolve或者reject,就不再执行
if (self.status !== "pending") {
return
}
// 将状态改为resolved
self.status = "resolved"
// 保存value数据
self.data = value
// 如果有待执行的callback函数,立即异步执行回调onResolved
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(callbacksObj => {
callbacksObj.onResolved(value)
})
})
}
}
function reject(reason) {
// 如果当前状态不是pending,直接结束
// 意思就是如果前面执行过resolve或者reject,就不再执行
if (self.status !== "pending") {
return
}
// 将状态改为rejected
self.status = "rejected"
// 保存value数据
self.data = reason
// 如果有待执行的callback函数,立即异步执行回调onRejected
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(callbacksObj => {
callbacksObj.onRejected(reason)
})
})
}
}
// 立即同步执行excutor
try {
excutor(resolve, reject)
} catch (error) { // 如果执行器抛出异常,promise对象变为rejected状态
reject(error)
}
}
// Promise原型对象的then()
// 指定成功和失败的回调函数,返回一个新的promise对象
Promise.prototype.then = function (onResolved, onRejected) {
//假设当前状态是pending状态,将回调函数保存起来
this.callbacks.push({onResolved, onRejected})
}
// Promise原型对象的catch()
// 指定失败的回调函数,返回一个新的promise对象
Promise.prototype.catch = function (onRejected) {
}
//Promsie函数对象resolve方法
// 返回一个指定结果的成功的promise
Promise.resolve = function(value) {
}
//Promsie函数对象reject方法
// 返回一个指定reason的失败的promise
Promise.reject= function(reason) {
}
//Promsie函数对象all方法
// 返回一个promise,只有当所有promise都成功才成功
// 只要有一个失败的就失败
Promise.all= function(promises) {
}
//Promsie函数对象race方法
// 返回一个promise,其结果由第一个完成的promise决定
Promise.race= function(promises) {
}
// 向外暴露Promise函数
window.Promise = Promise
})(window)
十四、自定义Promise.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义promise</title>
<style type="text/css">
</style>
</head>
<body>
<script src="./promise.js" type="text/javascript"></script>
<script type="text/javascript">
const p = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(1) // value
reject(2) // reason
}, 100)
})
p.then(value => {
console.log("onResolved1()", value)
}, reason => {
console.log("onRejected1()", reason)
})
p.then(value => {
console.log("onResolved2()", value)
}, reason => {
console.log("onRejected2()", reason)
})
</script>