什么是同步?什么是异步?
同步
直接能拿到结果,比如你去医院挂号,你拿到号才会离开窗口,同步任务可能消耗10ms,也有可能消耗3s,总之不拿到结果是不会离开的。
function f1() {
console.log("你好");
}
function f2() {
console.log("hello");
}
f1();
f2();
上面为同步代码,函数f2必须等f1执行完才能执行。
异步
如果不能直接拿到结果,比如你在餐厅门口等位置,你拿到号可以去逛街。
什么时候可以吃饭呢?
你可以每十分钟去餐厅询问(轮调)
你也可以用微信接收通知(回调)
比如下面代码:
function f1() {
setTimeout(() => {
console.log("你好");
}, 3000);
}
function f2() {
console.log("hello");
}
f1();
f2();
这个时候代码会直接先执行f2,等待最少3s才会执行函数f1,实际上是等待了3秒之后把f1放到了event queue里面,此时要等到主线程空闲的时候,才会执行event queue里面的f1函数。
js里面最基础的异步实现就是调用setTimeout,setInterval
回调(callback)
你写给自己的函数不是回调
你写给别人用的函数就是回调
比如request.onreadystatechange就是写给浏览器调用的
举个栗子:
function add(num1, num2, callback){
var sum = num1 + num2;
callback(sum);
}
function print(num){
console.log(num);
}
add(1, 2, print);
上面代码里就print是回调
异步和回调的关系
关联
异步任务需要的在得到结果时通知js来拿结果
让js留一个函数地址给浏览器
异步任务完成时浏览器调用该函数地址即可
同时把结果作为参数传给该函数
这个函数是我写给浏览器调用的,所以是回调函数
区别
异步任务需要用到回调函数来通知结果
但回调函数不一定只用在异步任务了,还可以用在同步任务里
array.forEach(n=>console.log(n))
判断同步还是异步
如果一个函数的返回值处于 setTimeout、ajax(即XMLHttpRequest)、AddEventListener这三个东西内部,那么这个函数就是异步函数。
Promise
如果异步任务有两个结果:成功和失败怎么办?
有两个方法:
1.回调接受两个参数
fs.readFile('./1.txt', (error, data)=>{
if(error){ console.log(' 失败'); return }
console.log(data.toString()) //成功
})
2.搞两个回调
ajax('get','/1.json', data=>{}, error=>{})
//前面是成功回调,后面是失败回调
ajax('get', '/1.json', {
success: ()=>{}, fail: ()=>{}
})
// 接受一个对象,对象有两个Key,表示成功和失败
但是不管是方法一还是方法二,都有问题:
1.代码不规范,名称五花八门
2.容易出现回调地狱
3.很难进行错误处理
怎么解决回调问题?
由此有人就提出了promise这种设计模式
它的用法:
return new Promise((resolve,reject)=>{...})
//任务成功时调用resolve(result)
//任务失败时调用reject(error)
resolve和reject会再调用成功和失败函数,它们只接收一个参数
使用.then(success,fail)传入成功和失败函数
Promise介绍
- Promise 不是前端发明的
- Promise 是目前前端解决异步问题的统一方案
- window.Promise 是一个全局函数,可以用来构造 Promise 对象
- 使用 return new Promise((resolve, reject)=> {}) 就可以构造一个 Promise 对象,构造出来的 Promise 对象含有一个 .then() 函数属性
- resolve 和 reject 并不是 .then(succes, fail) 里面的 success 和 fail,resolve 会去调用 success,reject 会去调用 fail
.then
.then()方法返回一个Promise实例。它最多包含两个参数:用于的成功和失败情况的回调函数Promise。
使用.then方法
var p1 = new Promise((resolve, reject) => {
resolve('Success!');
// or
// reject(new Error("Error!"));
});
p1.then(value => {
console.log(value); // Success!
}, reason => {
console.error(reason); // Error!
});
上面代码中,如果执行成功就会调用resolve(...), 当异步代码失败时就会调用reject(...),而 .then 中的函数参数就是上面调用resolve(...)方法传入的值。
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// proceed
});
上面的代码使用 then 方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。 如果前一个回调函数返回的是Promise对象,这时后一个回调函数就会等待该Promise对象有了运行结果,才会进一步调用。
.catch
用于指定发生错误时的回调函数。
getJSON("/posts.json").then(function(posts) {
// some code
}).catch(function(error) {
// 处理前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前两个回调函数的错误
});
Promise.all()
Promise.all(promiseArray)方法是Promise对象上的静态方法,该方法的作用是将多个Promise对象实例包装,生成并返回一个新的Promise实例。
参数:promiseArray,是一个Promise实例数组
var p1 = Promise.resolve(1),
p2 = Promise.resolve(2),
p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {
console.log(results); // [1, 2, 3]
});
在上面的方法中,promise数组中所有的promise实例都变为resolve的时候,该方法才会返回,并将所有结果传递results数组中。promise数组中任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的promise对象。reject使用示例如下:
var p1 = Promise.resolve(1),
p2 = Promise.reject(2),
p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {
//then方法不会被执行
console.log(results);
}).catch(function (e){
//catch方法将会被执行,输出结果为:2
console.log(e);
});
Promise.race()
顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
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'
})
前端小白,如有错误请留言指正!!!