前期准备
实例对象与函数对象
- 实例对象:new 函数产生的对象,称为实例对象,简称为对象
- 函数对象:将函数作为对象使用时,称为函数对象
- 函数对象.实例对象
function Fn() {} //Fn为函数
const fn = new Fn() //Fn称为构造函数,fn为实例对象
console.log(Fn.prototype) //Fn作为对象使用时,称为函数对象
Fn.bind({}) //Fn作为函数对象使用
$('#test') //$作为函数使用
$.get('/test') //$作为函数对象使用
同步回调与异步回调
同步回调
- 立即执行,完全执行完了才结束,不会放入回调队列中
- 数组遍历相关的回调 / Promise的executor函数
const arr = [1, 3, 5];
//遍历回调,同步回调,不放入队列,立即执行
arr.forEach(item => {
console.log(item);
}) //1 3 5
异步回调
- 不立即执行,放入回调队列中执行、
- 定时器回调 / ajax回调 / Promise成功或失败的回调
//定时器回调
setTimeout(() => {
console.log(1)
}, 0)
console.log(2) //2 1
// Promise 成功或失败的回调
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {console.log('value', value)},
reason => {console.log('reason', reason)}
)
console.log(2)
//2
//value 1
JS中的异常error处理
错误的类型
- Error:所有错误的父类型
- ReferenceError:引用的变量不存在
- TypeError:数据类型不正确
- RangeError:数据值不在其所允许的范围内
- SyntaxError:语法错误
错误处理(捕获与抛出)
- 抛出错误:throw error
function something() {
if (Date.now()%2===1) {
console.log('当前时间为奇数,可以执行任务')
} else { //如果时间为偶数抛出异常,由调用来处理
throw new Error('当前时间为偶数,无法执行任务')
}
}
- 捕获错误:try … catch
try {
something()
} catch (error) {
alert(error.message)
}
错误对象
- message属性:错误相关信息
- stack属性:函数调用栈记录信息
try {
let d
console.log(d.xx)
} catch (error) {
console.log(error.message)
console.log(error.stack)
}
console.log('已出错')
// Cannot read property 'xx' of undefined
// TypeError:Cannot read property 'xx' of undefined
// 已出错
// 因为错误被捕获处理了,后面的代码才能运行下去,打印出’已出错‘
Promise的理解与使用
Promise是什么
理解
- 抽象表达:Promise是JS中进行异步编程的新的解决方案(旧方案是单纯使用回调函数)
- 具体表达:
①从语法上看:Promise是一个构造函数 (自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法)
②从功能上看:promise对象用来封装一个异步操作并可以获取其成功/失败的结果值
状态
- 实例对象promise中的一个属性 PromiseState
1.pending 变为 resolved/fullfilled
2.pending 变为 rejected - 对象的状态不受外界影响
- 只有这两种,且一个 promise 对象只能改变一次
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果
- 无论成功还是失败,都会有一个结果数据。成功的结果数据一般称为 value,而失败的一般称为 reason
值
- 实例对象promise的另一个值 PromiseResult
- 保存着对象 成功/失败 的值(value/reason)
- resolve/reject可以修改值
基本流程![在这里插入图片描述](https://img-blog.csdnimg.cn/30df1efbd2df425dae895963d25688b5.png)
基本使用
- Promise构造函数接受一个函数(执行器函数)作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
- resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数value传递出去
- reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数error/reason传递出去
- Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
- then方法可以接受两个回调函数作为参数:
第一个回调函数onResolved()是Promise对象的状态变为resolved时调用
第二个回调函数onRejected()是Promise对象的状态变为rejected时调用
都接受Promise对象传出的值作为参数
为什么用Promise
指定回调函数的方式更加灵活
promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定)
支持链式调用,可以解决回调地狱问题
- 回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是其内部嵌套的回调函数执行的条件
- 解决方案:promise链式调用
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(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)
}
}
如何使用Promise
Promise构造函数
Promise (excutor){}
- executor函数:执行器 (resolve, reject) => {}
- resolve函数:内部定义成功时调用的函数 resove(value)
- reject 函数:内部定义失败时调用的函数 reject(reason)
- NOTE:executor 是执行器,会在 Promise 内部立即同步回调,异步操作 resolve/reject 就在 executor 中执行
Promise.prototype.then方法
p.then(onResolved, onRejected) => {}
- onResolved 函数:成功的回调函数 (value) => {}
- onRejected 函数:失败的回调函数 (reason) => {}
- NOTE:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 promise 对象
Promise.prototype.catch方法
p.catch(onRejected) => {}
- onRejected 函数:失败的回调函数 (reason) => {}
- 相当于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) // 失败的数据
}
)
Promise.resolve 方法
Promise.resolve(value)
- value:将被 Promise 对象解析的参数,也可以是一个成功或失败的 Promise 对象
- 返回:返回一个带着给定值解析过的 Promise 对象,如果参数本身就是一个 Promise 对象,则直接返回这个 Promise 对象
- 1.如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象
let p1 = Promise.resolve(520);
console.log(p1); // Promise {<fulfilled>: 521}
- 2.如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
resolve('OK') //成功的Promise
reject('Error');
}));
console.log(p2);
p2.catch(reason => {
console.log(reason);
}) //Error
Promise.reject 方法
Promise.reject(reason)
- reason:失败的原因
- 说明:返回一个失败的 promise 对象
Promise.all方法
Promise.all(iterable)
- iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String
- 说明:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败
Promise.race方法
Promise.race(iterable)
- 说明:返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态;谁先完成就输出谁(不管是成功还是失败)
关键问题
如何改变 promise 的状态
- resolve(value):如果当前是 pending 就会变为 resolved
- reject(reason):如果当前是 pending 就会变为 rejected
- 抛出异常:如果当前是 pending 就会变为 rejected
let p = new Promise((resolve, reject) => {
resolve(1); //pending => fulfilled(resolved)
reject(2); //pending => rejected
throw '出问题了'; // 抛出异常,promise变为rejected失败状态,reason为抛出的error
})
能否执行多个回调
当 promise 改变为对应状态时都会调用
let p = new Promise((resolve, reject) => {
reject(77)
});
p.then(
value => {}
reason => {console.log('reason', reason)}
)
p.then(
value => {}
reason => {console.log('reason2', reason)}
)
// reason 77
// reason2 77
改变 promise 状态和指定回调函数谁先谁后
都有可能,常规是先指定回调再改变状态,但也可以先改状态再指定回调
- 先改状态再指定回调
1.在执行器中直接调用 resolve()/reject()
2.延迟更长时间才调用 then()
let p = new Promise((resolve, reject) => {
// setTimeout(() => {
resolve('OK');
// }, 1000); // 有异步就先指定回调,否则先改变状态
});
p.then(value => {
console.log(value);
},reason => {}
)
- 什么时候才能得到数据
1.如果先指定的回调,那当状态发生改变时,回调函数就会调用得到数据
2.如果先改变的状态,那当指定回调时,回调函数就会调用得到数据
promise.then() 返回的新 promise 的结果状态
- 简单表达:由 then() 指定的回调函数执行的结果决定
let p = new Promise((resolve, reject) => {
resolve('ok');
});
let result = p.then(value => {
console.log(value);
}, reason => {
console.log(reason);
});
console.log(result);
- 如果抛出异常,新 promise 变为 rejected,reason 为抛出的异常
let p = new Promise((resolve, reject) => {
resolve('ok');
});
let result = p.then(value => {
throw '错误';
}, reason => {
console.log(reason);
});
console.log(result);
- 如果返回的是非 promise 的任意值,新 promise 变为 resolved,value 为返回的值
let p = new Promise((resolve, reject) => {
resolve('ok');
});
let result = p.then(value => {
return 777;
}, reason => {
console.log(reason);
});
console.log(result);
- 如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果
let p = new Promise((resolve, reject) => {
resolve('ok');
});
let result = p.then(value => {
return new Promis((resolve, reject) => {
reject('error');
});
}, reason => {
console.log(reason);
});
console.log(result);
串联多个任务
- promise 的 then() 返回一个新的 promise,可以并成 then() 的链式调用
- 通过 then 的链式调用串联多个同步/异步任务
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
});
p.then(value => {
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => {
console.log(value); // success
}).then(value => {
console.log(value); // undefined
})
异常穿透
- 当使用 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('onRejected1()', reason)
}
)
// onRejected1() 1
如何中断Promise
- 当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的回调函数
- 办法:在回调函数中返回一个 pending 状态的 promise 对象
return new Promise(() => {});
自定义(手写)Promise
整体结构框架
//声明构造函数
function Promise(executer) {
//保存当前实例对象的this的值
const self = this;
//添加属性
// 给promise对象指定status属性,初始值为pending
self.PromiseState = PENDING;
//给promise对象指定一个用于存储结果数据的属性
self.PromiseResult = null;
// 存的是对象 每个元素的结构:{onResolved() {}, onRejected() {}}
self.callbacks = [];
//resolve函数
function resolve(value) {
//判断状态
if (self.PromiseState !== PENDING) return;
//1.修改对象状态(promiseState)
self.PromiseState = RESOLVED; //resolved
//2.设置对象结果值(promiseResult)
self.PromiseResult = value;
// 如果有待执行的callback函数,立即【异步】执行回调函数onResolved
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(callbackObj => {
callbackObj.onResolved(value);
})
}, 0);
}
}
//reject函数
function reject(reason) {
//判断状态
if (self.PromiseState !== PENDING) return;
//1.修改对象状态(promiseState)
self.PromiseState = REJECTED;
//2.设置对象结果值(promiseResult)
self.PromiseResult = reason;
// 如果有待执行的callback函数,立即【异步】执行回调函数onResolved
if (self.callbacks.length > 0) {
setTimeout(() => {
self.callbacks.forEach(callbackObj => {
callbackObj.onRejected(reason);
})
}, 0);
}
}
try {
//同步调用 执行器函数
executer(resolve, reject);
} catch (error) {
//修改promise对象状态为失败
reject(error);
}
}
Promise.prototype.then
//添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
//指定默认的成功的回调onResolved (向后传递成功的value)
onResolved = typeof onResolved === 'function' ? onResolved : value => value
// 指定默认的失败的回调onRejected(向后传递失败的reason 实现错误/异常传透的关键点)
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const self = this
return new Promise((resolve, reject) => {
function handle(callback) {
try {
// result获取回调函数执行(return)的结果
const result = callback(self.PromiseResult);
if (result instanceof Promise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
}
//判断pending状态
if (this.PromiseState === PENDING) {
self.callbacks.push({
onResolved(value) {
handle(onResolved);
},
onRejected(reason) {
handle(onRejected);
}
})
} else if (self.PromiseState === RESOLVED) {
setTimeout(() => {
handle(onResolved)
}, 0)
} else {
setTimeout(() => {
handle(onRejected)
}, 0)
}
})
}
Promise.prototype.catch
//添加catch方法
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected);
}
Promise.resolve
//添加resolve方法
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(resolve, reject);
} else {
resolve(value);
}
})
}
Promise.reject
//添加reject方法
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
Promise.all
//添加all方法
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
//声明计数变量
let count = 0;
const values = new Array(promises.length);
for (let i = 0; i < promises.length; i++) {
// 防止数组中有不是promise的元素
Promise.resolve(promises[i]).then(value => {
//对象状态成功+1
count++;
//将当前promise对象成功的结果存入数组
values[i] = value;
//如果每个promise对象都成功
if (count === promises.length) {
resolve(values);
}
}, reason => {
reject(reason);
})
}
})
}
Promise.race
//添加race方法
static resolveDelay(value, timeout) {
// 延时返回一个成功/失败的promise
return new Promise((resolve, reject) => {
setTimeout(() => {
if (value instanceof Promise) {
value.then(resolve, reject)
} else { // value不是promise => promise状态变为成功,数据是value
resolve(value)
}
})
}, timeout);
}
static rejectDelay(reason, timeout) {
// 延时返回一个失败的promise
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(reason)
}, timeout)
})
}