同步与异步
最早JS是用来处理页面交互的,也就是DOM操作,这就造成了JS必须是只能单线程的。否则如果有两个线程同时操作了DOM该怎么办呢?所以在JS执行过程中只有一个线程执行代码。那出现耗时任务怎么办呢?JS提出了异步模式。但他依然是单线程。
同步模式:代码依次执行,怎么写的怎么执行,上一行执行完执行下一行。按序执行。存在问题:如果有一行任务很大时间很长,就会给用户一种卡死的感觉。
异步模式:不会等待任务结束再执行,而是直接开始执行下一个。最后通过回调函数解决。会有一个任务队列,调用栈空时就开始执行消息队列。
实现异步语法:回调函数——根本方案搞清楚步骤,什么完了做什么。函数作为参数,定义好了,再去执行。
Promise
Promise是ES6引入的异步编程的新解决方案。
console.dir(Promise)
语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
Promise 构造函数: Promise(excutor) {}
Promise.prototype.then 方法
Promise.prototype.catch
基本用法
//实例化 Promise 对象
const p = new Promise(function(resolve, reject) {
setTimeout(function(){
// let data = '数据库中的用户数据';
// resolve(data);
let err = '数据读取失败';
reject(err);
}, 1000);
});
//调用 promise 对象的 then 方法
p.then(function(value) {
console.log(value);
}, function(reason){
console.error(reason);
})
生命周期
每个Promise都会经历一个短暂的生命周期:先是处于进行中(pending) 的状态,此时操作尚未完成,所以它也是未处理(unsettled)的;一旦异步操作执行结束,Promise则变为已处理(settled)的状态。
内部属性[[Promisestate]]被用来表示Promise的3种状态:“pending”、"fulfilled"及”rejected”。这个属性不暴露在Promise对象上,所以不能以编程的方式检测Promise的状态,只有当Promise的状态改变时,通过then()方法来釆取特定的行动。
Promise.then() 方法
所有Promise都有then()方法,它接受两个参数:第一个是当Promise的状态变为fulfilled时要调用的函数,与异步操作相关的附加数据都会传递给这个完成函数(fulfilment function);第二个是当Promise的状态变为rejected时。
要调用的函数,其与完成时调用的函数类似,所有与失败状态相关的附加数据都会传递给这个拒绝函数(rejection function)。
链式调用/串联Promise
每次调用then()方法或catch()方法时实际上创建并返回了另一个Promise, 只有当第一个Promise完成或被拒绝后,第二个才会被解决。
误区:嵌套使用promise then里边继续嵌套函数
Promise提供了链式调用,最大程度的避免回调嵌套,因为then方法也返回的是一个Promise对象,所以可以不断的进行.then()。
Promise对象的then方法会返回一个全新的Promise对象,后面的then方法会为上一个返回的then进行注册回调,前面的then方法中回调函数的返回值会作为后面then方法回调的参数,如果回调中返回的是Promise,那后面的then方法回调会等待它的结束。也可以catch()指定失败回调,这个catch 是指定上一个then返回的Promise的异常,不捕获第一个Promise。Promise的异常会一直传递,但是每个then的第二个参数只是捕获当前的Promise。
then返回的Promise
then方法的返回结果是由then里边的回调函数的返回结果决定的。
1、如果返回结果是非Promise的类型,返回的Promise状态为成功并且返回值是return的值,如果不写return 则undefined。
2、如果是Promise,返回的Promise的值和状态都是内部Promise的值和状态。但是这里是新的Promise。
3、抛出错误,状态失败值是抛出的值。
静态方法
Promise.resolve() // 快速转换为Promise。
Promise.reject() // 快速生成一个失败的Promise。
Promise.all([]) // 同步执行多个Promise,返回新的Promise.all()捕获全部结束的时机。
Promise.race() // 同步执行多个Promise,返回第一个结束的Promise并且终止其他。
Promise.all()方法
Promise.all()方法只接受一个参数并返回一个Promise,该参数是一个含有多个受监视Promise的可迭代对象(例如,一个数组),只有当可迭代对象中 所有Promise都被解决后返回的Promise才会被解决,只有当可迭代对象中所有Promise都被完成后返回的Promise才会被完成。
Promise.race()方法
Promise.race()方法监听多个Promise的方法稍有不同:它也接受含多个受监视Promise的可迭代对象作为唯一参数并返回一个Promise,但只要有一个Promise被解决返回的Promise就被解决,无须等到所有Promise都被完成。一旦数组中的某个Promise被完成,Promise.race()方法也会像Promise.all() 方法一样返回一个特定的Promise。
Promise应用
解决跨域:www.jianshu.com/p/e87b02128…
免费的接口:www.jianshu.com/p/f180deefd…
封装AJAX
原生的AJAX
//1. 创建对象
const xhr = new XMLHttpRequest();
//2. 初始化
xhr.open("GET", "http://baike.baidu.com/api/openapi/BaikeLemmaCardApi?scope=103&format=json&appid=379020&bk_key=你好&bk_length=600");
//3. 发送
xhr.send();
//4. 绑定事件, 处理响应结果
xhr.onreadystatechange = function () {
//判断
if (xhr.readyState === 4) {
//判断响应状态码 200-299 表示成功
if (xhr.status >= 200 && xhr.status < 300) {
//表示成功
console.log(xhr.response);
} else {
//如果失败
console.error(xhr.status);
}
}
}
Promise封装的AJAX
const p = new Promise((resolve, reject) => {
//1. 创建对象
const xhr = new XMLHttpRequest();
//2. 初始化
xhr.open("GET", "http://baike.baidu.com/api/openapi/BaikeLemmaCardApi?scope=103&format=json&appid=379020&bk_key=你好&bk_length=600");
//3. 发送
xhr.send();
//4. 绑定事件, 处理响应结果
xhr.onreadystatechange = function () {
//判断
if(xhr.readyState === 4) {
//判断响应状态码 200-299
if (xhr.status >= 200 && xhr.status < 300) {
//表示成功
resolve(xhr.response);
} else {
//如果失败
reject(xhr.status);
}
}
}
})
//指定回调
p.then(function(value) {
console.log('v成功',value);
}, function(reason) {
console.error('失败',reason);
})
解决回调地狱
假设现在有这样一个场景,我们根据小明的身份证号获取小明的基础信息,根据小明的基础信息的地址查询小明的家乡景区,根据景区获取景区的详细介绍。最后用基础信息和家乡介绍生成小明的自我介绍。
ES5的写法
function getIntroduce(cardId) {
setTimeout(() => {
const personInfo = {
id: cardId,
name: '小明',
address: '北京',
age:19
}
console.log(personInfo);
setTimeout(() => {
const Attractions = ['故宫', '长城', '颐和园']
console.log(Attractions);
setTimeout(() => {
const sebnceIntro = Attractions[0] + '是一个五A级景区,他的历史悠久,记载着很多。。。';
console.log(sebnceIntro);
const res = `我叫${personInfo.name},今年${personInfo.age}来自${personInfo.address},我的家乡有谱很多名胜古迹,比如${ Attractions.join(',') }等等,其中${ sebnceIntro }希望大家有机会常来玩`;
console.log(res);
}, 1000)
}, 3000)
}, 200)
};
getIntroduce(1)
Promise的写法
function getIntroduce(cardId) {
const personInfo = new Promise((resolve,reject)=>{
const info = {
id: cardId,
name: '小明',
address: '北京',
age:19
}
resolve({info,text:`我叫${info.name},今年${info.age}来自${info.address}。`})
})
const Attractions = personInfo.then(value=>{
const list = ['故宫', '长城', '颐和园']
return {
text:value.text+`我的家乡有谱很多名胜古迹,比如${
list.join(',')
}`,
Attractions:list
}
})
const info = Attractions.then(value=>{
const sebnceIntro = value.text + value.Attractions[0] + '是一个五A级景区,他的历史悠久,记载着很多。。。';
return {text:value.text+`等等,其中${sebnceIntro}希望大家有机会常来玩`}
})
info.then(value => {
console.log(value.text);
})
}
getIntroduce(1)
手写Promise
Promise的需求是构造函数,两个回调函数参数改变state,then判断状态,拿到参数,执行对应的回调,链式调用。Promise异步与多次调用处理,在cbs中值得穿透,race/all 返回Promise。
const pendding = 'pendding';
const fulfilled = 'fulfilled';
const rejected = 'rejected';
class myPromise {
constructor(exct) {
this.state = pendding;
this.value = undefined
this.reason = undefined;
this.successCbs = [];
this.rejectCbs = [];
const resolve = (value) => {
if(this.state == pendding) {
this.state = fulfilled;
this.value = value;
this.successCbs.forEach(cb => cb())
}
}
const reject = (reason) => {
if (this.state == pendding) {
this.state = rejected;
this.reason = reason;
this.rejectCbs.forEach(cb=>cb())
}
}
try {
exct(resolve, reject)
} catch (e) {
console.log(e);
reject(e)
}
};
then(successCb= value => value, rejectCb = reason => { throw reason }) {
return new myPromise((resolve , reject) => {
if (this.state == fulfilled) {
try {
let res = successCb(this.value);
resolvePromise(res, resolve, reject);
} catch (e) {
console.log(e);
reject(e)
}
} else if (this.state == rejected) {
try {
let res = rejectCb(this.reason);
resolvePromise(res, resolve, reject)
} catch (e) {
console.log(e);
reject(e)
}
} else {
this.rejectCbs.push(() => {
try {
let res = rejectCb(this.value);
resolvePromise(res, resolve, reject)
} catch (e) {
console.log(e);
reject(e)
}
});
this.successCbs.push(() => {
try {
let res = successCb(this.value);
resolvePromise(res, resolve, reject)
} catch (e) {
console.log(e)
reject(e)
}
})
}
})
};
static all(array) {
let result = [];
let index = 0;
return new myPromise((resolve, reject) => {
debugger;
let addResult = (key,value) => {
result[key] = value;
index++;
if (index == array.length) {
debugger;
resolve(result)
}
}
for (let i = 0; i < array.length; i++) {
debugger;
let current = array[i];
if (current instanceof myPromise) {
current.then(res => addResult(i,res),reason=>reject(reason))
} else {
addResult(i,current)
}
}
})
}
static resolve(target) {
if (value instanceof myPromise) return value;
return new myPromise(v=>v(value))
}
static race(array) {
return new myPromise((resolve, reject) => {
for (let i = 0; i < array.length; i++) {
let current = array[i];
if (current instanceof myPromise) {
current.then(res => resolve(res),reason=>reject(reason));
} else {
resolve(current)
}
}
})
}
};
function resolvePromise(value, resolve, reject) {
if (value instanceof myPromise) return value.then(resolve, reject);
return resolve(value)
}
// test
// let p = new myPromise((res, rej) => {
// res('成功')
// })
// p.then((value) => {
// console.log(value)
// return new myPromise((v) => v('111') )
// }).then((v) => {
// setTimeout(()=>console.log(2, v), 3000)
// }, (v) => {
// console.log('2失败', v)
// })
let pall = myPromise.all([
'1',
new myPromise((s1) => {
setTimeout(() => s1('2'), 1000);
}),
'3',
new myPromise((s) => { s('4') }),
'5'
]);
let prace = myPromise.race([
new myPromise((s1) => {
setTimeout(() => s1('2'), 0);
}),
new myPromise((s1) => {
setTimeout(() => s1('3'), 1000);
}),
// new myPromise((s) => { s('4') }),
]);
console.log(pall);
prace.then((res) => {
console.log('xxxxx',res);
})