共计 8930 个字符,预计需要花费 23 分钟才能阅读完成。
以前学习写的笔记,感觉还不错,当初收回来,心愿对你有帮忙。
1. 前置常识
1.1 区别实例对象与函数对象
实例对象:new
函数产生的对象, 称为实例对象, 简称为对象
函数对象:将函数作为对象应用时, 简称为函数对象
function Fn() {}
const fn = new Fn() // fn 为实例对象
Fn.bind({}) // Fn 为函数对象
1.2 两种类型的回调函数
同步回调
- 了解:立刻执行, 齐全执行完了才完结, 不会放入回调队列中
- 例子: 数组遍历相干的回调函数 / Promise 的 excutor 函数
异步回调
- 了解:不会立刻执行, 会放入回调队列中未来执行
- 例子:定时器回调 / ajax 回调 / Promise 的胜利 | 失败的回调
const arr = [1, 2, 3]
arr.forEach(item => console.log(item)) // 同步回调, 不会放入回调队列, 而是立刻执行
console.log('forEatch() 之后')
setTimeout(() => { // 异步回调, 会放入回调队列, 所有同步执行完后才可能执行
console.log('timout 回调')
}, 0)
console.log('setTimeout 之后')
1.3 JS 的 error 解决
谬误的类型
- Error:所有谬误的父类型
-
ReferenceError:援用的变量不存在
console.log(a) // ReferenceError: a is not defined
-
TypeError:数据类型不正确的谬误
let b = null console.log(b.xxx) // TypeError: Cannot read property 'xxx' of null
-
RangeError:数据值不在其所容许的范畴内
function fn() {fn() } fn() // RangeError: Maximum call stack size exceeded
-
SyntaxError:语法错误
let c = """" // SyntaxError: Unexpected string
错误处理
- 捕捉谬误:try … catch
- 抛出谬误:throw error
error 对象的构造
- message 属性:谬误相干信息
- stack 属性:函数调用栈记录信息
2. Promise 是什么?
2.1 了解
形象表白:Promise 是 JS 中进行异步编程的新的解决方案(旧的是谁?=> 纯回调的模式)
具体表白:
- 从语法上来说:Promise 是一个构造函数
- 从性能上来说:Promise 对象用来封装一个异步操作并能够获取其后果
2.2 Promise 的状态扭转
Promise 的状态扭转只有这 2 种:
且一个 Promise 对象只能扭转一次,无论变成胜利还是失败,都会有一个后果数据,胜利的后果数据个别称为 value
,失败的后果数据个别称为 reason
。
2.3 Promise 根本流程
2.4 Promise 的根本应用
示例,如果以后工夫是偶数就代表胜利,否则代表失败
// 1. 创立一个新的 Promise 对象
const p = new Promise((resolve, reject) => { // 执行器函数,同步执行
// 2. 执行异步操作工作
setTimeout(() => {const time = Date.now() // 如果以后工夫是偶数就代表胜利,否则代表失败
// 3.1 如果胜利了,调用 resolve(value)
if (time % 2 === 0) {resolve('胜利的数据,value =' + time)
} else {// 3.2 如果失败了,调用 reject(reason)
reject('失败的数据,reason =' + time)
}
}, 1000);
})
p.then(value => {
// 承受失去胜利的 value 数据,专业术语:onResolved
console.log('胜利的回调', value)
}, reason => {
// 承受失去失败的 reason 数据,专业术语:onRejected
console.log('失败的回调', reason)
})
3. 为什么要用 Promise?
3.1 指定回调函数的形式更加灵便
旧的:回调函数必须在启动异步工作前指定
// 胜利的回调函数
function successCallback(result) {console.log('解决胜利:' + result)
}
function failureCallback(error) {console.log('解决失败:' + error)
}
// 应用纯回调函数
createAudioFileSync(audioSettings, successCallback, failureCallback)
Promise:启动异步工作 => 返回 Promise 对象 => 给 Promise 对象绑定回调函数,甚至能够在异步工作完结后指定多个
// 应用 Promise
const promise = createAudioFileSync(audioSettings)
setTimeout(() => {promise.then(successCallback, failureCallback)
}, 3000);
3.2 反对链式调用,能够解决回调天堂问题
什么是回调天堂?回调函数嵌套调用,内部回调函数异步执行的后果是嵌套的回掉执行条件,代码是程度向右扩大
// 回调天堂
doSomething(function(result) {doSomethingElse(result, function(newResult) {doThirdThing(newResult, function(finalResult) {console.log('Got the final result:' + finalResult)
}, failureCallback)
}, failureCallback)
},
回调天堂的毛病:不便浏览,不便于异样解决
解决方案:Promise 链式调用,代码程度向下扩大
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)
终极解决方案: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)
}
}
4. Promise 的 API 阐明
4.1 API 阐明
Promise 构造函数 。
Promise (excutor) {}
,excutor 会在 Promise 外部立刻同步回调, 异步操作在执行器中执行
- excutor 函数:执行器
(resolve, reject) => {}
- resolve 函数:外部定义胜利时咱们调用的函数
value => {}
- reject 函数:外部定义失败时咱们调用的函数
reason => {}
Promise.prototype.then 办法
(onResolved, onRejected) => {}
,指定用于失去胜利 value 的胜利回调和用于失去失败 reason 的失败回调返回一个新的 promise 对象
- onResolved 函数:胜利的回调函数
(value) => {}
- onRejected 函数:失败的回调函数
(reason) => {}
Promise.prototype.catch 办法
(onRejected) => {}
,onRejected 函数:失败的回调函数 (reason) => {}
,then() 的语法糖, 相当于:then(undefined, onRejected)
Promise.resolve 办法
(value) => {}
,value:胜利的数据或 promise 对象,返回一个胜利 / 失败的 promise 对象
Promise.reject 办法
(reason) => {}
,reason:失败的起因,返回一个失败的 promise 对象
Promise.all 办法
(promises) => {}
,promises:蕴含 n 个 promise 的数组,返回一个新的 promise, 只有所有的 promise 都胜利才胜利, 只有有一个失败了就间接失败
Promise.race 办法
(promises) => {}
,promises: 蕴含 n 个 promise 的数组,返回一个新的 promise, 第一个实现的 promise 的后果状态就是最终的后果状态
// 产生一个胜利值为 1 的 Promise 对象
const p1 = new Promise((resolve, reject) => {resolve(1)
})
// 产生一个胜利值为 2 的 Promise 对象
const p2 = Promise.resolve(2)
// 产生一个失败值为 3 的 Promise 对象
const p3 = Promise.reject(3)
p1.then(value => console.log(value))
p2.then(value => console.log(value))
p3.catch(reason => console.error(reason))
// const pAll = Promise.all([p1, p2])
const pAll = Promise.all([p1, p2, p3])
pAll.then(values => {console.log('all onResolved()', values) // all onResolved() [ 1, 2]
}, reason => {console.log('all onRejected()', reason) // all onRejected() 3})
const race = Promise.race([p1, p2, p3])
race.then(value => {console.log('all onResolved()', value)
}, reason => {console.log('all onRejected()', reason)
})
4.2 Promise 的几个关键问题
4.2.1 如何扭转 Promise 的状态
resolve(value),如果以后是 pendding 就会变为 resolved
reject(reason),如果以后是 pendding 就会变为 rejected
抛出异样 ,如果以后是 pendding 就会变为 rejected
const p = new Promise((resolve, reject) => {// resolve(1) // Promise 变为 resolved 胜利状态
// reject(2) // Promise 变为 rejected 失败状态
// Promise 变为 rejected 失败状态,reason 为抛出的 error
throw new Error('我抛出的异样')
// 变为 rejected 失败状态,reason 为抛出的 3
// throw 3
})
p.then(value => {},
reason => {console.log('reason :', reason); }
)
4.2.2 当一个 promise 指定多个胜利 / 失败回调函数, 都会调用吗?
当 promise 扭转为对应状态时都会调用
const p = new Promise((resolve, reject) => {
// 变为 rejected 失败状态,reason 为抛出的 3
throw 3
})
p.then(value => {},
reason => {console.log('reason :', reason); }
)
p.then(value => {},
reason => {console.log('reason2 :', reason); }
)
// 后果:// reason : 3
// reason2 : 3
4.2.3 扭转 promise 状态和指定回调函数谁先谁后?
都有可能, 失常状况下是先指定回调再扭转状态, 但也能够先改状态再指定回调。
如何先改状态再指定回调?
- 在执行器中间接调用 resolve()/reject()
- 提早更长时间才调用 then()
什么时候能力失去数据?
- 如果先指定的回调, 那当状态产生扭转时, 回调函数就会调用, 失去数据
- 如果先扭转的状态, 那当指定回调时, 回调函数就会调用, 失去数据
// 惯例:先指定回调函数, 后扭转状态
new Promise((resolve, reject) => {setTimeout(() => {resolve(1) // 后扭转状态(同时指定数据),异步执行回调函数
}, 1000);
}).then( // 先指定回调函数,保留以后指定的回调函数
value => {},
reason => {console.log('reason :', reason); }
)
// 先改状态, 后指定回调函数
new Promise((resolve, reject) => {resolve(1) // 先扭转状态(同时指定数据)}).then( // 后指定回调函数,异步执行回调函数
value => {console.log('value2:', value);},
reason => {console.log('reason2 :', reason); }
)
const p = new Promise((resolve, reject) => {resolve(1) // 先扭转状态(同时指定数据)})
setTimeout(() => {
p.then(value => { console.log('value3:', value);},
reason => {console.log('reason3 :', reason); }
)
}, 1500);
4.2.4 promise.then() 返回的新 promise 的后果状态由什么决定?
简略表白:由 then() 指定的回调函数执行的后果决定
具体表白:
- 如果抛出异样, 新 promise 变为 rejected, reason 为抛出的异样
- 如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值
- 如果返回的是另一个新 promise, 此 promise 的后果就会成为新 promise 的后果
new Promise((resolve, reject) => {resolve(1)
}).then(
value => {console.log('onResolved1()', value); // 1
// return 1.1 或
return Promise.resolve(1.1)
// return Promise.reject(1.1)
// throw 1.1
},
reason => {console.log('onRejected1()', reason);
}
).then(value => { console.log('onResolved2()', value); }, // 1.1
reason => {console.log('onRejected2()', reason) } // 1.1
)
4.2.5 promise 如何串连多个操作工作
promise 的 then()
返回一个新的 promise, 能够开成 then()
的链式调用,通过 then 的链式调用串连多个同步 / 异步工作。
4.2.6 promise 异样传透
当应用 promise 的 then 链式调用时, 能够在最初指定失败的回调,后面任何操作出了异样, 都会传到最初失败的回调中解决。
上面的示例代码演示了异样传透
new Promise((resolve, reject) => {// resolve(1)
reject(1)
}).then(
value => {console.log('onResolved1()', value);
return 2
}
).then(
value => {console.log('onResolved2()', value);
return 3
}
).then(
value => {console.log('onResolved3()', value);
}
).catch(
reason => {console.log('onRejected()', reason); // onRejected() 1}
)
代码会执行 .catch
中的代码,但实际上代码的执行不是执行到第 3 行就间接跳转到 catch 外面了,而是从第一个 then 调用向下一个个的执行(逐级传递),然而因为咱们 then 外面没有解决异样。在 then 外面没写解决异样实际上相当于默认增加了 reason => {throw reason}
或者 reason => Promise.reject(reason)
:
new Promise((resolve, reject) => {reject(1)
}).then(value => { console.log('onResolved1()', value); },
// reason => {throw reason}
// 或者
reason => Promise.reject(reason)
)
Promise 的异样传透示意图
4.2.7 中断 promise 链
当应用 promise 的 then 链式调用时, 在两头中断, 不再调用前面的回调函数。
方法: 在回调函数中返回一个 pendding
状态的 promise 对象
new Promise((resolve, reject) => {resolve(1)
}).then(
value => {console.log('onResolved1()', value);
return new Promise(() => {}) // 返回一个 pending 的 Promise,中断 promise 链
}
).then( // 这个 then 不会执行力
value => {console.log('onResolved2()', value); }
)
5. async 与 await
Async/await 实际上只是一种基于 promises 的糖衣语法糖,Async/await 和 promises 一样,都是非梗塞式的,Async/await 让异步代码更具同步代码格调,这也是其劣势所在。
async function
用来定义一个返回AsyncFunction
对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的Promise
返回其后果,。如果你在代码中应用了异步函数,就会发现它的语法和构造会更像是规范的同步函数。MDN async_functionawait
操作符用于期待一个Promise
对象。它只能在异步函数async function
中应用。MDN await
5.1 async 函数
async
函数的返回值为 Promise
对象,async
函数返回的 Promise
的后果由函数执行的后果决定
async function fn1() {return 1}
const result = fn1()
console.log(result) // Promise {1}
在控制台能够看见如下信息
既然是 Promise 对象,那么咱们用 then 来调用,并抛出谬误,执行 onRejected()
且 reason 为错误信息为“我是谬误”
async function fn1() {
// return 1
// return Promise.resolve(1)
// return Promise.reject(2)
throw '我是谬误'
}
fn1().then(value => { console.log('onResolved()', value) },
reason => {console.log('onRejected()', reason) } // onRejected() 我是谬误)
5.2 await 表达式
await
右侧的表达式个别为 promise
对象, 但也能够是其它的值:
- 如果表达式是
promise
对象,await
返回的是promise
胜利的值 - 如果表达式是其它值, 间接将此值作为
await
的返回值
function fn2() {return new Promise((resolve, reject) => {setTimeout(() => {resolve(1000)
}, 1000);
})
}
function fn4() { return 6}
async function fn3() {// const value = await fn2() // await 右侧表达式为 Promise,失去的后果就是 Promise 胜利的 value
// const value = await '还能够这样'
const value = await fn4()
console.log('value', value)
}
fn3() // value 6
await
必须写在 async
函数中, 但 async
函数中能够没有 await
,如果 await
的 Promise
失败了, 就会抛出异样, 须要通过 try...catch
捕捉解决
function fn2() {return new Promise((resolve, reject) => {setTimeout(() => {// resolve(1000)
reject(1000)
}, 1000);
})
}
async function fn3() {
try {const value = await fn2()
} catch (error) {console.log('失去失败的后果', error)
}
}
fn3() // 失去失败的后果 1000
5.3 Async/await 比 Promise 更优越的体现
简洁洁净 ,应用 async/await 能省去写多少行代码
错误处理 ,async/wait 能用雷同的构造和好用的经典 try/catch 解决同步和异步谬误,谬误堆栈能指出蕴含谬误的函数。
调试 ,async/await 的一个极大劣势是它更容易调试,应用 async/ await 则无需过多箭头函数,并且能像失常的同步调用一样间接跨过 await 调用。
笔记首发于我的公众号《前端全栈开发者》