同步与异步
同步就是能直接拿到结果,比如在医院挂号室,拿到号才能离开窗口。
异步就是不能直接拿到结果,比如在餐厅等餐时还可以去逛街。你可以每10分钟去餐厅问一下(轮询),你也可以扫码用微信接收通知(回调)
回调
异步与回调的关系:
异步任务需要在得到结果时通知JS来拿结果
怎么通知呢?
可以让JS留一个函数地址(电话号码)给浏览器
异步任务完成时浏览器调用该函数地址即可(拨打电话)
同时把结果作为参数传给该函数(电话里说可以来吃了)
这个函数是我写给浏览器调用的,所以是回调函数
注意:
异步不一定只用回调可以用轮询,回调不一定只在异步任务里面,也可以在同步任务里面。
function f1() {console.log("我是回调")}function f2(fn) {console.log("我是函数不是回调")fn()}f2(f1)
上面的f1没有被调用,但是f1传给了f2 ,f2调用了f1。f1就是写给f2调用的函数。那么f1就是回调。
判断同步与异步(初级)
如果一个函数的返回值处于以下几个内部那么就是异步函数:
setTimeout()AJAXAddEventListener()
绝对不能让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 } = optionsconst request27 = newXMLHttpRequest()request.open(method, url)request.onreadystatechange = () => {if (request.readyState === 4) {//成功就调用resolve, 失败就调用rejectif (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) {// 两个请求现在都执行完成}));
