写在前面:
我是「沸羊羊_」,昵称来自于姓名的缩写 fyy ,之前呕心沥血经营的博客因手残意外注销,现经营此账号。
本人是个小菜,正向着全栈工程师的方向努力着,文章可能并不高产,也很基础,但每写一篇都在用心总结,请大佬勿喷。
如果您对编程有兴趣,请关注我的动态,一起学习研究。
感谢每位读者!
同步
异步
异步请求在江湖中的地位非常重要,异步给JS带来了更多的可能,我们是不是遇到过这样的业务场景:后一个请求依赖前一个请求的结果,也就是一个请求依赖另一个请求,如果依赖层数再多一点,很容易出现回调地狱的现象。本文将介绍几种异步请求的处理方式,拭目以待。
知乎推荐回答:你去商场买衣服,刚好你的 size 没有了,于是你把电话留给了店员,过几天,那件衣服补货了,店员给你打电话,你去店里把它买下来。在这个例子里,你的电话号码就是回调函数,你把电话留下叫做登记回调函数,店里补货了这个事件触发了回调函数,店员给你打电话是调用回调函数,你去店里买下来是响应回调事件。
回调函数就是 任何一个 被以该回调函数为其参数的其他方法调用的方法。
//callback
//读./pakage.json的内容,写入./p.json文件,读取成功 2s后打印"ok"
const fs = require('fs')
fs.readFile('./pakage.json',(err,info) => {
fs.writeFile('./p.json',info,(err) => {
if(!err) {
setTimeout(() => {
console.log('ok')
},2000)
}
})
})
以上代码用来实现异步操作,没有什么问题,但如果业务不只是这两个文件,那还要很多个回调函数的嵌套吗?这样的代码可维护性,可读性都变得很差,这就是回调地狱。
Promise对象,返回一个“异步承诺”,不管是请求成功还是出错都会执行,promise 对象带有 resolve 和 reject 方法,可以请求结果进行分开处理。promise对象有三种状态:
promise的一个便利是,针对多个异步请求的情况,可以使用 Promise.all 方法来确保各个请求的执行返回,避免了“回调地狱”。
pending 状态可以转换为 fulfilled 或者 rejected ,并且只能转换一次,如果 pending 转化为其中一种状态,就不能转换为另一种状态了,并且 fulfilled 和 rejected 状态只能由 pending 转化而来,两者之间不能互相转换。
<script src="https://cdn.bootcss.com/bluebird/3.5.1/bluebird.min.js"></script>//如果低版本浏览器不支持Promise,通过cdn这种方式
<script type="text/javascript">
function loadImg(src) {
var promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
img.onload = function () {
resolve(img)
}
img.onerror = function () {
reject('图片加载失败')
}
img.src = src
})
return promise
}
var src = 'https://www.imooc.com/static/img/index/logo_new.png'
var result = loadImg(src)
result.then(function (img) {
console.log(1, img.width)
return img
}, function () {
console.log('error 1')
}).then(function (img) {
console.log(2, img.height)
})
</script>
promise 可以做更多事情,比如,有若干异步任务,需要先做任务1,成功后做任务2,任何任务失败则不再继续并进行错误处理,要实现这样的异步任务,如果不用 Promise对象,需要一层一层的嵌套回调。有了 Promise ,我们只需要简单的写
job1.then(job2).then(job3).catch(handleError);
then 方法可以被同一个 promise 调用多次,返回一个新的 promise 对象,因此可以通过链式调用 then 方法,避免了地狱回调的嵌套回调。
试想一个页面聊天系统,需要从两个不同的URL获取用户的个人信息和好友列表,这两个任务是可以并行执行的,用 Promise.all() 实现如下。
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 获得一个Array: ['P1', 'P2']
});
有些时候,同时发起两个请求获取信息,只需要获取先返回的数据即可,这种情况下,用 Promise.race()实现。
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
console.log(result); // 'P1'
});
可能 p1 执行较快,promise 的 then() 将获得结果 p1,p2 仍在继续执行,但执行结果将被丢弃。
总结:
将上面例子做下修改,加深对这两者的理解:
var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
var result1 = loadImg(src1)
var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'
var result2 = loadImg(src2)
Promise.all([result1, result2]).then(function (datas) {
console.log('all', datas[0])//
console.log('all', datas[1])//
})
Promise.race([result1, result2]).then(function (data) {
console.log('race', data)//
})
虽然promise 很便利了,但在代码简洁方面,还有待优化,因此由了 async/await ,在多个请求情况下更是可以“链式”声明,可以大大节省代码量。
Async/Await 是什么?
async本意是异步,而 await 可以认为是 async wait 的缩写,所以可以理解为:async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。
另外,语法规定,await 只能出现在 async 函数中。
async起什么作用?
async 函数是如何处理它的返回值的?我们写一个 demo 测试一下
async function testAsync() {
return "hello async";
}
const result = testAsync();
console.log(result);//Promise { 'hello async' }
async 函数返回的是一个 promise 对象,如果在函数中 return ,async 会通过 Promise.resolve() 封装成 Promise 对象。但如果 不 return ,会出现什么情况,很显然,它会返回 :Promise.resolve(undefined)
Promise 的特点是:无等待,所以在没有 await 的情况下执行 async 函数,会立即执行,返回一个 promise 对象,绝不会阻塞后面的语句,这和普通的 promise 对象函数没啥区别,关键在于 await 做了什么?
await 在等待一个 async 函数完成,因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值,如果 await 等到了要等的东西,比如说是一个 promise 对象,await 会阻塞后面的代码,等待 promise 对象 resolve ,然后得到 resolve 的值,作为 await 表达式的运算结果。
var step1 = async function () {
return new Promise(function (resolve, reject) {
let st = setTimeout(() => {
resolve('step1');
}, 1000);
});
}
var step2 = function () {
return new Promise(function (resolve, reject) {
let st = setTimeout(() => {
resolve('step2')
}, 1000);
});
}
var step3 = function () {
return new Promise(function (resolve, reject) {
let st = setTimeout(() => {
resolve('step3')
}, 1000);
})
}
async function test() {
var data1 = await step1();
console.log(data1);
var data2 = await step2();
console.log(data2);
var data3 = await step3();
console.log(data3)
}
test();