参考文档
一、什么是promise
Promise对象用于异步操作,它表示一个尚未完成且预计在未来完成的异步操作
二、同步和异步
我们知道,JavaScript的执行环境是「单线程」。
所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任务,这个任务执行完后才能执行下一个,它会「阻塞」其他任务。这个任务可称为主线程。
但实际上还有其他线程,如事件触发线程、ajax请求线程等。
这也就引发了同步和异步的问题
2-1 同步
同步模式,即单线程模式,一次只能执行一个任务,函数调用后需等到函数执行结束,返回执行的结果,才能进行下一个任务。如果这个任务执行的时间较长,就会导致「线程阻塞」。
var x = true;
while(x);
console.log("don't carry out"); //不会执行
其中的while是一个死循环,它会阻塞进程,因此第三句console不会执行。
2-2 异步
异步模式,即与同步模式相反,可以一起执行多个任务,函数调用后不会立即返回执行的结果,如果任务A需要等待,可先执行任务B,等到任务A结果返回后再继续回调。
最常见的异步模式就数定时器了
setTimeout(function() {
console.log('taskA, asynchronous');
}, 0);
console.log('taskB, synchronize');
//输出
taskB, synchronize
taskA, asynchronous
2-3 回调函数
- 上例中,setTimeout里的function便是回调函数。可以简单理解为:(执行完)回(来)调(用)的函数
- 可以看出,回调函数是一段可执行的代码段,它以「参数」的形式传递给其他代码,在其合适的时间执行这段(回调函数)的代码。
- 也就是说,回调函数不仅可以用于异步调用,一般同步的场景也可以用回调。在同步调用下,回调函数一般是最后执行的。而异步调用下,可能一段时间后执行或不执行(未达到执行的条件)。
三、为什么使用Promise
解决回调地狱,将异步的代码以同步的方式显示出来,简化代码,方便阅读
使用Promise,我们就可以利用then进行「链式回调」,将异步操作以同步操作的流程表示出来。
//使用Promise之前的回调地狱
request('test1.html', '', function(data1) {
console.log('第一次请求成功, 这是返回的数据:', data1);
request('test2.html', data1, function (data2) {
console.log('第二次请求成功, 这是返回的数据:', data2);
request('test3.html', data2, function (data3) {
console.log('第三次请求成功, 这是返回的数据:', data3);
//request... 继续请求
}, function(error3) {
console.log('第三次请求失败, 这是失败信息:', error3);
});
}, function(error2) {
console.log('第二次请求失败, 这是失败信息:', error2);
});
}, function(error1) {
console.log('第一次请求失败, 这是失败信息:', error1);
});
//使用Promise后
sendRequest('test1.html', '').then(function(data1) {
console.log('第一次请求成功, 这是返回的数据:', data1);
return sendRequest('test2.html', data1);
}).then(function(data2) {
console.log('第二次请求成功, 这是返回的数据:', data2);
return sendRequest('test3.html', data2);
}).then(function(data3) {
console.log('第三次请求成功, 这是返回的数据:', data3);
}).catch(function(error) {
//用catch捕捉前面的错误
console.log('sorry, 请求失败了, 这是失败信息:', error);
});
四、Promise的基本用法
4-1 基本用法
Promise对象代表一个未完成、但预计将来会完成的操作。
它有以下三种状态:
- pending:初始值,不是fulfilled,也不是rejected
- fulfilled:代表操作成功
- rejected:代表操作失败
Promise有两种状态改变的方式,既可以从pending转变为fulfilled,也可以从pending转变为rejected。一旦状态改变,就「凝固」了,会一直保持这个状态,不会再发生变化。当状态发生变化,promise.then绑定的函数就会被调用
//构建Promise
var promise = new Promise(function (resolve, reject) {
if (/* 异步操作成功 */) {
resolve(data);
} else {
/* 异步操作失败 */
reject(error);
}
});
类似构建对象,我们使用new来构建一个Promise。Promise接受一个「函数」作为参数,该函数的两个参数分别是resolve和reject。这两个函数就是就是「回调函数」,由JavaScript引擎提供
resolve函数的作用:在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
4-2 基本API
.then()
语法:Promise.prototype.then(onFulfilled, onRejected)
.catch()
语法:Promise.prototype.catch(onRejected)
该方法是.then(undefined, onRejected)的别名,用于指定发生错误时的回调函数。
promise.then(function(data) {
console.log('success');
}).catch(function(error) {
console.log('error', error);
});
/*******等同于*******/
promise.then(function(data) {
console.log('success');
}).then(undefined, function(error) {
console.log('error', error);
});
var promise = new Promise(function (resolve, reject) {
throw new Error('test');
});
/*******等同于*******/
var promise = new Promise(function (resolve, reject) {
reject(new Error('test'));
});
//用catch捕获
promise.catch(function (error) {
console.log(error);
});
-------output-------
Error: test
五、新版ajax本身就是一个Promise
封装ajax
var baseUrl="https://music.aityp.com/"
function http(url){
return new Promise((resolve,reject)=>{
$.ajax({
url:baseUrl+url,
type:"get",
success:res=>{
resolve(res)
},
error:err=>{
reject(err)
}
})
})
}
//调用
<script>
http("top/playlist?cat=华语").then(res=>{
let id=res.playlists[0].id;
return id
}).then(res=>{
http(`playlist/detail?id=${id}`).then(res=>{
let id=res.playlists.trackIds[0].id;
return id
}).then(res=>{
http(`song/url?id=${res}`).then(res=>{
console.log(res)
})
})
})
</script>
六、async/await
6-1 关于async
async/await相比较Promise 对象then 函数的嵌套,与 Generator 执行的繁琐(需要借助co才能自动执行,否则得手动调用next() ), Async/Await 可以让你轻松写出同步风格的代码同时又拥有异步机制,更加简洁,逻辑更加清晰。
6-2 async特点
- async/await更加语义化,async 是“异步”的简写,async function 用于申明一个 function 是异步的; await,可以认为是async wait的简写, 用于等待一个异步方法执行完成;
- async/await是一个用同步思维解决异步问题的方案(等结果出来之后,代码才会继续往下执行)
- 可以通过多层 async function 的同步写法代替传统的callback嵌套
6-3 async语法
如果函数前面加上async关键字,函数就会变成一个promise 可以通过then触发
async function go(){
return 1;
}
/* then去触发执行 */
go().then(res=>{
console.log(res) //1
})
6-4 async关键字
await后面一定要跟promise,执行一个promise
await一定是在一个async里面 只在async里面有效
async function go(){
return 1;
}
async function getRes(){
var data=await go();
console.log(data) //1
}
getRes()
await关键字可以获取promise的结果
async function getCat(){
return "华语"
}
async function detail(){
return "detail"
}
async function url(){
return "url"
}
async function getRes(){
var cat=await getCat();
console.log(cat) //"华语"
var de=await detail();
console.log(de) //"detail"
var ur=await url(); //"url"
console.log(ur)
}
getRes()
6-5 async嵌套
用forEach遍历数组,不能把异步的变为同步,在遍历前面加await,使变为同步
await就是跟在他后面的promise执行完毕之后,才会执行下面的代码
<script>
async function go(){
return "go"
}
var arr=['html','css','js','vue']
async function test(){
console.log(1);
await arr.forEach(async item=>{
var data=await go();
console.log(data)
})
console.log(2) //输出顺序:1 go() 2
}
test()
/* console.log(1);
arr.forEach(item=>{
console.log(item)
})
console.log(2) */
</script>
七、手写一个Promise
<script>
//Promise有几种状态? pending fulfilled rejected
//Promise有哪些方法? resolve reject
//Promise有哪些属性? value reason
//Promise有一个then方法
class Promise {
constructor(fn) {
this.state = "pending";
this.value = null;
this.reason = null;
this.then = function (onFulfilled, onRejected) {
//执行then方法的时候,需要查看Promise的状态,如果Promise执行成功,需要执行成功的的回调;如果Promise失败,需要执行Promise失败的回调
switch (this.state) {
case 'fulfilled':
onFulfilled(this.value);
break;
case 'reject':
onRejected(this.reason);
break;
}
};
let resolve = value => {
if (this.state === 'pending') {
this.state = "fulfilled";
this.value = value;
}
}
let reject = reason => {
if (this.state === 'pending') {
this.state = "reject";
this.reason = reason;
}
}
//执行fn函数
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
}
var p = new Promise(function (resolve, reject) {
console.log("22222")
resolve("小明");
})
//Promise.then方法接收两个函数做为参数
//第一个函数参数是Promise成功的回调
//第二个函数参数是Promise失败的回调
p.then((msg) => {
console.log(msg,"AAAAAAAAAA")
}, (err) => {
console.log(err,"BBBBBBBBBBBBBBB");
})
</script>
八、Promise对象是什么
Promise对象是ES6( ECMAScript 2015 )对于异步编程提供的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
Promise本身不是异步的,只不过Promise中可以有异步任务,new Promise()的第一个函数参数是立马执行的。
function func1(a){
return new Promise((resolve,reject) => {
if(a > 10){
resolve(a)
}else{
reject(b)
}
})
};
func1('11').then(res => {
console.log('success');
}).catch(err => {
console.log('error');
})
//Promise构造函数接受一个函数作为参数,该函数的两个参数分别resolve 和 reject。它们是两个函数,由
JavaScript 引擎提供。
1.resolve函数的作用是: 将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 fulfilled),
在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
2.reject函数的作用是: 将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),
在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
//Promise对象实例的方法,then 和 catch:
1 .then方法: 用于指定调用成功时的回调函数。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例),因此可以采用链式写法,
即then方法后面再调用另一个then方法。
2 .catch方法: 用于指定发生错误时的回调函数。