同步与异步
同步就是能直接拿到结果,比如在医院挂号室,拿到号才能离开窗口。
异步就是不能直接拿到结果,比如在餐厅等餐时还可以去逛街。你可以每10分钟去餐厅问一下(轮询),你也可以扫码用微信接收通知(回调)
回调
异步与回调的关系:
异步任务需要在得到结果时通知JS来拿结果
怎么通知呢?
可以让JS留一个函数地址(电话号码)给浏览器
异步任务完成时浏览器调用该函数地址即可(拨打电话)
同时把结果作为参数传给该函数(电话里说可以来吃了)
这个函数是我写给浏览器调用的,所以是回调函数
注意:
异步不一定只用回调可以用轮询,回调不一定只在异步任务里面,也可以在同步任务里面。
function f1() {
console.log("我是回调")
}
function f2(fn) {
console.log("我是函数不是回调")
fn()
}
f2(f1)
上面的f1
没有被调用,但是f1
传给了f2
,f2
调用了f1
。f1
就是写给f2
调用的函数。那么f1
就是回调。
判断同步与异步(初级)
如果一个函数的返回值处于以下几个内部那么就是异步函数:
setTimeout()
AJAX
AddEventListener()
绝对不能让AJAX改成同步的。
function rollTheDice(fn) {
setTimeout(() => {
fn(parseInt(Math.random() * 6) + 1)
}, 1000) // 随机返回1到6的数字
}
rollTheDice((x) => {
console.log(x)
})
方法一:回调接受2个参数
fs.readFile('./text',(error ,data)=>{
if(error){ console.log('失败');return}
console.log(data.toString())
})
方法二:用2个回调
ajax('get','/1.json',data=>{},error=>{})
ajax('get','/1.json',{
success:()=>{}.fail:()=>{}
})
回调的问题
- 不够规范,名称五花八门
- 容易出现回调地狱,回调地狱就是我们异步任务中嵌套异步任务一层一层的,导致我们的代码臃肿,而promise链式调用解决这种代码问题。
- 很难出现错误处理
getUser((user) => {
getGroups(user, (groups) => {
groups.forEach((g) => {
g.filter((x) => x.ownerId === user.Id).forEach((x) =>
console.log(x)
)
})
})
})
Promise
Promise
对象是由关键字 new
及其构造函数来创建的。该构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolve
和 reject
——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve
函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject
函数。
return new Promsie((resolve,reject)=>{...})
实例
let myFirstPromise = new Promise(function(resolve, reject){
//当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
//在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
setTimeout(function(){
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function(successMessage){
//successMessage的值是上面调用resolve(...)方法传入的值.
//successMessage参数不一定非要是字符串类型,这里只是举个例子
console.log("Yay! " + successMessage);
});
return new Promise((resolve,rejec)=>{...})
- 任务成功则调用
resolve(result)
- 任务失败则调用
reject(error)
resolve
和reject
会再去调用成功和失败函数- 使用
.then(success, fail)
传入成功和失败函数
ajax = (method, url, options) => {
return new Promise((resolve, reject) => {
const { success, fail } = options
const request27 = newXMLHttpRequest()
request.open(method, url)
request.onreadystatechange = () => {
if (request.readyState === 4) {
//成功就调用resolve, 失败就调用reject
if (request.status < 400) {
resolve.call(null, request.response)
} else if (request.status >= 400) {
reject.call(null, request)
}
}
}
request.send()
})
}
但是封装的JAXA不能POST ;不能添加状态码
你对 Promise 的了解?
- Promise 不是前端发明的
- Promise 是目前前端解决异步问题的统一方案
- window.Promise 是一个全局函数,可以用来构造 Promise 对象
- 使用 return new Promise((resolve, reject)=> {}) 就可以构造一个 Promise 对象
- 构造出来的 Promise 对象含有一个 .then() 函数属性
.then() 与.catch()
.then()
- then方法提供一个供自定义的回调函数,若传入非函数,则会忽略当前then方法。
- 回调函数中会把上一个then中返回的值当做参数值供当前then方法调用。
- then方法执行完毕后需要返回一个新的值给下一个then调用(没有返回值默认使用undefined)。
- 每个then只可能使用前一个then的返回值。
.catch()
在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。
也就是说当函数里面出现错误是 JS不会停止运行,而是将错误传给.catch()。然后继续执行口面的回调。
function getNumber() {
var p = new Promise(function (resolve, reject) {
//做一些异步操作
setTimeout(function () {
var num = Math.ceil(Math.random() * 10) //生成1-10的随机数
if (num <= 5) {
resolve(num)
} else {
reject("数字太大了")
}
}, 1000)
})
return p
}
getNumber()
.then((data) => {
console.log("成功了")
console.log(data)
})
.catch((data) => {
console.log("失败了,错误传过来,继续执行")
console.log(data)
})
.then(() => {
console.log("绕开了,继续执行")
})
.all
谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都算返回Promise对象
Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。看下面的例子:
let Promise1 = new Promise(function(resolve, reject){})
let Promise2 = new Promise(function(resolve, reject){})
let Promise3 = new Promise(function(resolve, reject){})
let p = Promise.all([Promise1, Promise2, Promise3])
p.then(funciton(){
// 三个都成功则成功
}, function(){
// 只要有失败,则失败
})
有了all,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,是不是很酷?有一个场景是很适合用这个的,一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后,我们再进行页面的初始化。
.race
谁跑的快,以谁为准执行回调
race的使用场景:比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作,代码如下:
//请求某个图片资源
function requestImg(){
var p = new Promise((resolve, reject) => {
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = '图片的路径';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()])
.then((data) =>{
console.log(data);
})
.catch((err) => {
console.log(err);
});
requestImg函数会异步请求一张图片,我把地址写为”图片的路径”,所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。
由此可见:Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了。
jQuery.ajax
type
类型: String
请求方式 (“POST” 或 “GET”), 默认为 “GET”。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。
url
类型: String
发送请求的地址 (默认: 当前页面地址)。
username
类型: String
于响应HTTP访问认证请求的用户名
// 发出请求后立即分配处理程序,
// 并记住此请求的jqxhr对象
var jqxhr = $.ajax( "example.php" )
.done(function() { alert("success"); })
.fail(function() { alert("error"); })
.always(function() { alert("complete"); });
// perform other work here ...
// 为上述请求设置另一个完成功能
jqxhr.always(function() { alert("second complete"); });
axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
执行 GET
请求
// 为给定 ID 的 user 创建请求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 上面的请求也可以这样做
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
执行 POST
请求
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
执行多个并发请求
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// 两个请求现在都执行完成
}));