Promise是异步编程的一种解决方案
学习promise前我们先来了解下什么是异步?
基本概念:
首先我们要知道js是单线程语言,也就是说,它并不能像JAVA语言那样,多个线程并发执行。
单线程:
- JavaScript就是一个单线程的语言
- 为什么js是单线程?如果一个线程在一个节点中添加内容,另一个线程要删除这个节点。所以为了不必要的麻烦,js就是一门单线程语言。
先讲下什么是同步。同步指的是代码一行一行依次执行,下面的代码必须等待上面代码的执行完成。当遇到一些耗时比较长的任务时,比如网络请求就容易发生阻塞,必须等数据请求过来后才能执行后面的操作。
那么这里异步执行是在请求数据的同时还能执行后面的任务。异步是非阻塞的,异步逻辑与主逻辑相互独立,主逻辑不需要等待异步逻辑完成,而是可以立刻继续下去
这就相当于异步任务被对应的模块解析好了会自动放入消息队列,等待事件循环调入主线程执行(前提是主线程任务全部执行完毕)
。console.log("遇到煮饭任务,将饭放入电饭煲");
//使用setTimeout模拟煮饭
setTimeout(()=>{
console.log("饭已经煮熟,等待饭被取出");
},0);
console.log("开始炒菜任务");
解释疑惑:
JavaScript既然是单线程语言,那么为什么同时可以执行多个任务(同时煮饭炒菜)?
你可能会想这不是废话吗,煮饭交给电饭煲了啊。
确实没错,煮饭任务交给电饭煲了,那么在JS中是谁去处理这些异步的任务呢?
前面异步任务分析有说到,异步任务会被对应模块解析(饭被电饭煲模块解析)。
那么这就和宿主有关系了,js一般都是运行在游览器上(当然有node.js),也就是寄生在游览器上,那么宿主就是游览器。所以是宿主提供的模块去处理这些异步任务,使得JS可以实现与其他语言类似的多线程操作。
补充异步任务执行顺序:
所以从上可以看出异步的执行是依赖于回调函数,那么在进行异步操作时回调函数会带来什么影响呢?那就是回调地狱。
回调地狱指的是:回调函数嵌套回调函数,形成的多层嵌套。
例子:查询三次网络请求,请求成功一次才会发起下一个请求
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
$.ajax({
type: "post",
url: "https://tenapi.cn/v2/yiyan",
success(data) {
console.log("第一次成功查询信息:", data);
$.ajax({
type: "post",
url: "https://tenapi.cn/v2/yiyan",
success(data) {
console.log("第二次成功查询信息", data);
$.ajax({
type: "post",
url: "https://tenapi.cn/v2/yiyan",
success(data) {
console.log("第三次成功查询信息", data);
},
error(error) {
console.log("第三次成功信息出现异常了:" + error);
}
});
},
error(error) {
console.log("第二次成功信息出现异常了:" + error);
}
});
},
error(error) {
console.log("第一次成功信息出现异常了:" + error);
}
});
</script>
异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理且更强大
var promise = new Promise(传一个函数);
var promise = new Promise(function (resolve, reject) {
if (/* 异步操作成功 */) {
resolve(value);
} else {
reject(error);
}
});
有三个状态:
var promise = new Promise(function (resolve, reject) {
if (false) {
resolve('success');
} else {
reject('fail');
}
});
promise.then(res => {
console.log(res) // 成功 resolve('success')
}).catch(err => {
console.log(err) // 失败 reject('fail');
});
当promise状态发生改变,就会触发then()里的响应函数处理后续步骤
状态改变,只有两种可能:
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值
顾名思义, Promse.race就是赛跑的意思,意思就是说, Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态
<script>
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.race([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 打开的是 'failed'
})
</script>
如上代码就是产生了回调地狱,当代码过多会非常复杂。如下就是使用一种优雅的方式(promise)来解决如上的问题
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
//使用Promise解决回调地狱
function post(url,n) { //自己定义一个方法整合一下
return new Promise((resolve, reject) => {
$.ajax({
url: url,
success: function (data) {
resolve(data);
},
error: function (err) {
reject(`第${n}次调用失败`+err)
}
})
});
}
post("https://tenapi.cn/v2/yiyan",1)
.then((data) => {
console.log("第一次成功查询信息:", data)
return post("https://tenapi.cn/v2/yiyan",2);
})
.then((data) => {
console.log("第二次成功查询信息:", data)
return post("https://tenapi.cn/v2/yiyan",3);
})
.then((data) => {
console.log("第三次成功查询信息", data)
})
.catch((err) => { //失败的话catch
console.log( err)
});
</script>
总结
Promis就是对异步操作进行封装,其中的思想就是不希望你在回调函数中处理需要执行的代码,而是把回调执行的代码放入对应的区域也就是then()或catch()方法中处理,然后通过resolve()或reject()来调用。将代码分离后做的时链式的调用,你可以这样理解一个then或一个catch就是一个链条的连接点。一般有异步操作我们都要使用promise对异步操作进行封装,养成良好的习惯。
也是用来处理异步的,其实是Generator 函数的改进,背后原理就是promise
<script>
async function f1() {
return "abc";
}
console.log(f1())
</script>
// async
async function f1() {
return "abc";
// 自动包装成promise对象
// 与下面两种等价
// return Promise.resolve('abc');
// return new Promise((resolve) => { resolve('abc') });
}
f1().then((data)=>{
console.log("执行了异步",data)
})
function f4() {
setTimeout(() => {
console.log("222")
}, 3000)
console.log("333")
}
function f5() {
console.log("111")
f4();
console.log("444")
}
f5()
async function f4() {
await new Promise(function (resolve, reject) {
setTimeout(() => {
resolve()
}, 3000)
}).then((data)=>{
console.log("222")
})
console.log("333")
}
function f5() {
console.log("111")
f4();
console.log("444")
}
f5()
async function f4() {
await new Promise(function (resolve, reject) {
setTimeout(() => {
resolve()
}, 3000)
}).then((data)=>{
console.log("222")
})
console.log("333")
}
async function f5() {
console.log("111")
await f4();
console.log("444")
}
f5()
async function f3() {
return Promise.reject('sss');
// return Promise.resolve('abc')
}
async function f5() {
try {
var c = await f3().then((data)=>{ console.log(data)});
} catch (e) {
console.log(e)
}
//如果await 是reject,后面代码就不会执行,要加上try catch才会执行
}
f5()