js 异步解决方案

js的异步请求历来被诟病,但是社区和规范一直也在努力,这里简单说下这些变化。

ajax

严格地说ajax属于与服务器交换数据的API,与异步并不完全相同。但对于早期的前端来说,异步的操作基本都是与ajax交涉的过程。

2005年ajax-new-approach-web-application一文催生了ajax技术,随后各浏览器纷纷实现了XmlHttpRequest。其具有划时代的意义,为网页性能和用户体验带来了巨大的提升。下面是一个基于XmlHttpRequest对象实现的简单取数据的函数:

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){if(xhr.readyState === 4){
    if(xhr.status == 200){
	console.log(xhr.responseText)
    }
}}
xhr.open('GET','https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState',true)
xhr.send(null)

可以看出这个对象具有浓浓的面向对象的风格,没有函数式编辑的优雅。目前作为XHR的替代api——fetch,则改正了这一股风气。

jquery deffered

这玩意已经退出历史舞台了,我差不多已经忘记了。但是17年去头条面试还居然问我$deffered是怎么实现的,要写代码。我表示很崩溃。

promise

promise 是es6的规范,它的规则如下:
promise的参数是一个函数,这个函数执行时会被注入两个参数,resolve和reject,这两个函数会改变promise对象的状态,状态发生变化时则会执行相应的回调
具体有三个状态:

  • pending
  • fullfilled
  • rejected

状态转换规则如下:

所以对于一个连续三次的异步操作,它的代码可能如下:

function resolveNumAfter2S(x){
	return new Promise(resolve=>{
		setTimeout(()=>{resolve(x)}, 500)
	})
}

resolveNumAfter2S(10)
.then(x => {return resolveNumAfter2S(x+1)})
.then(x => {return resolveNumAfter2S(x+2)})
.then(x => {console.log(x)})

promise 隐藏了中间一些抽象的处理,resolve,reject导致的状态变化以及它们的实现,then,catch的绑定都隐藏了,所以理解起来会有些困难,如果自己用js写一个类似的实现,则能容易理解它的功能,下面是一个小意思:

const MyPromise = function(f){
	f.bind(this, this._resolve.bind(this), this._reject.bind(this))()
}

MyPromise.prototype = Object.assign(MyPromise.prototype, {
	status: 'pending',
	_resolve: function (data) {
		this.status = 'fullfilled';
		this._then(data);
	},
	_reject: function (err) {
		this.status = 'rejected';
		this._catch(err);
	},
	then: function (f) {
		this._then = f;
		return this;
	},
	catch: function (f) {
		this._catch = f;
		return this;
	}
})


function resolveAfter2s(x){
	return new MyPromise((reslove,reject)=>{
		setTimeout(()=>{
			reject("err")
		}, 2000)
	})
}

var res = resolveAfter2s(10).then(x=>{
	console.log(x)
}).catch(err => {
	console.log(err)
})

async await

作为es7的终级异步解决方案,async/await的规则如下:

  • 使用async前缀定义的函数,会返回一个promise对象
  • await只能在async function 中使用
  • 遇到await 指令,异步代码的执行和同步代码一样,不会跳过

对于一个简单的例子,大约很难体会它到底有什么用, 比如:

function resolveNumAfter2S(x){
	return new Promise(resolve=>{
		setTimeout(()=>{resolve(x)}, 2000)
	})
}

async function getNum(){
	return await resolveNumAfter2S(1)
}

getNum().then(x=>{
	console.log(x)
})

但考虑以下场景,就会发现其方便之处:

function resolveNumAfter2S(x){
	return new Promise(resolve=>{
		setTimeout(()=>{resolve(x)}, 500)
	})
}
// 假设你有一个需求,需要请求三个异步的数据,而每一步都要依赖上一步的数据,你完全可以以同步的代码体验去写代码:
async function getNum(){
	let a = await resolveNumAfter2S(6);
	let b = await resolveNumAfter2S(a);
	let c = await resolveNumAfter2S(b+a);

	return a+b+c;
}

getNum().then(x=>{
	console.log(x)
})
posted @ 2018-01-16 19:41  ViCanary  阅读(144)  评论(0编辑  收藏  举报