异步与Promise浅析

158 阅读5分钟

同步与异步

同步就是能直接拿到结果,比如在医院挂号室,拿到号才能离开窗口。

异步就是不能直接拿到结果,比如在餐厅等餐时还可以去逛街。你可以每10分钟去餐厅问一下(轮询),你也可以扫码用微信接收通知(回调)

回调

异步与回调的关系:

异步任务需要在得到结果时通知JS来拿结果

怎么通知呢?

可以让JS留一个函数地址(电话号码)给浏览器

异步任务完成时浏览器调用该函数地址即可(拨打电话)

同时把结果作为参数传给该函数(电话里说可以来吃了)

这个函数是我写给浏览器调用的,所以是回调函数

注意:

异步不一定只用回调可以用轮询,回调不一定只在异步任务里面,也可以在同步任务里面。

function f1() {
console.log("我是回调")
}
function f2(fn) {
console.log("我是函数不是回调")
fn()
}
f2(f1)

上面的f1没有被调用,但是f1传给了f2 ,f2调用了f1f1就是写给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:()=>{}
})

回调的问题

  1. 不够规范,名称五花八门
  2. 容易出现回调地狱,回调地狱就是我们异步任务中嵌套异步任务一层一层的,导致我们的代码臃肿,而promise链式调用解决这种代码问题。
  3. 很难出现错误处理
			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)的函数作为它的参数。这个“处理器函数”接受两个函数——resolvereject ——作为其参数。当异步任务顺利完成且返回结果值时,会调用 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)
  • resolvereject会再去调用成功和失败函数
  • 使用.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("绕开了,继续执行")
				})
		

由此可见:Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了。

jQuery.ajax

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中文文档

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) {
    // 两个请求现在都执行完成
  }));

资料来源:饥人谷

本文为贰贰的原创文章,著作权归本人和饥人谷所有,转载务必注明来源