最近呢?第一主要是加班比较严重,还被高中同桌嘲讽:屌丝程序员,还讽刺我头发掉的快等等;第二呢 我最近和大学室友准备开一个开源项目,最近正在疯狂的筹划中,你能想象 设计:自己人、后台:自己人、前端:自己人、ui没有的痛苦吗?但是我们还是想做一个出来。哈哈,反正我们这种屌丝的想法不要钱。😂😂😂 好了,不哔哔了,这篇文章主要是来研究异步操作的。如果做过Android端的都知道,Android端有rxjava、多线程、aysnctask等好多东西来处理同步和异步的问题,但是初涉前端,很多东西都是朦胧的,我就由浅及深谈一谈我所理解的异步操作。
##说在前头:
同步和异步呢?是程序员难以理解的一个东西,其实我对同步、异步也是略窥门径。我就简单说说我了解的同步、异步。
说起同步、我在这里把同步异步来做一个小例子帮助大家理解:
背景:小明暗恋着小红
同步:
就好比:小明约小红去吃饭,小明必须得到小红的响应才能做下一步动作,不然小明会在处于等待状态。
异步:
就好比:小红约小明去吃饭。小红对着空气喊了一声“小明,我去吃饭了”,然后小红就扬长而去了,不需要得到小明的回应。
总的来说,同步异步就只需要知道四个概念:
- 同步 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。=
- 异步 异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。=
- 阻塞 阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。
- 非阻塞 非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
一、黄阶下级斗技:胡搞一通,能运行就好了
我们刚开始入门的时候,经常会用一种完成任务的心态去实现功能。就例如现在页面启动的时候有N个请求,这就很有可能会写成这个样子:
{
function get1(){
console.log("get1...")
setTimeout(function(){
post1()
},2000)
}
function get2(){
console.log("get2...")
setTimeout(function(){
post2()
},2000)
}
function post1(){
console.log("post1...")
setTimeout(function(){
get2()
},2000)
}
function post2(){
console.log("post2...")
setTimeout(function(){
console.log("render...")
},2000)
}
get1()
}
复制代码
运行结果如下
{
function get1(){
console.log("get1...")
setTimeout(function(){
console.log("get1 end...")
},1000)
}
function get2(){
console.log("get2...")
setTimeout(function(){
console.log("get2 end...")
},1000)
}
function post1(){
console.log("post1...")
setTimeout(function(){
console.log("post1 end...")
},1000)
}
function post2(){
console.log("post2...")
setTimeout(function(){
console.log("post2 end and render...")
},1000)
}
get1()
get2()
post1()
post2()
}
复制代码
运行结果如下:
那既然js内部自己就已经开始在使用异步了,我们为什么不好好来研究一波异步操作呢?
##二、 黄阶上级斗技:回调 如果你和我一样,都有一点java/android的底子,就一定会对回调有某种执念,同样js内部也可以完成这种写法,具体代码如下:
{
function get1(...callback){
console.log("get1...")
setTimeout(function(){
console.log("get1 end...")
for(let index of callback){
index()
}
},1000)
console.log("do something...")
}
function get2(){
console.log("get2...")
setTimeout(function(){
console.log("get2 end...")
},1000)
}
function post1(){
console.log("post1...")
setTimeout(function(){
console.log("post1 end...")
},1000)
}
function post2(){
console.log("post2...")
setTimeout(function(){
console.log("post2 end and render...")
},1000)
}
get1(get2,post1,post2)
}
复制代码
运行结果如下:
当然回调不一定只用于异步,一般同步(阻塞)的场景下也经常用到回调,比如要求执行某些操作后执行回调函数。
{
function f1(callback){
console.log("do something=======>");
// do somethings
(callback && typeof(callback) === "function")&& callback();
}
function f2(){
console.log("do others things======>");
}
f1(f2)
}
复制代码
关于回调呢?就算这么多,具体的思想前面也说了,es5以前回调是处理异步的主要方式。当然现在都2018年,es也出了很多的新方法,我来首先介绍一个promise。 ##三、 玄阶中级斗技:promise 说起promise这个东西,其实我在刚开始接触到前端的时候就有用到,主要是用来异步操作网络请求。首先介绍的是promise的两个特点:
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
pomise内存实现了两个方法:resolve、reject。resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
{
//模拟一个网络请求
let promise = new Promise((res,rej)=>{
console.log("promise start====>")
let data = Math.random()*10
setTimeout(()=>{
if(data>2){
res("promise end sussess!!")
}else{
rej("promise end fail!!")
}
},3000)
})
promise.then(res=>{
console.log(res)
},res=>{
console.log(res)
})
}
复制代码
效果图如下:
new
出来之后就开始执行,里面会收到两个回调--
resolve
、
reject
。我们这里把知道触发这两个回调的状态叫做
pending
,一旦触发两个回调之后就会立刻就会凝固。
{
//模拟一个网络请求
let promise = new Promise((res,rej)=>{
console.log("promise start====>");
throw new Error("test error")
})
promise.then(res=>{
console.log(res)
},res=>{
console.log(res.message)
})
}
复制代码
reject
本身就是可以拦截错误的。如果在promise中出错之后会走
reject
回调。但是我在看很多人的源码,很多大佬是这么写的:
{
let promise = new Promise((res,rej)=>{
console.log("promise start====>");
throw new Error("test error")
res("promise end sussess!!")
})
promise.then(res=>{
console.log(res)
}).catch(res=>{
console.log(res.message)
})
}
复制代码
刚开始我没有想通,但是当我写了很多东西之后就开始逐渐明白了。如下面的例子
{
let promise = new Promise((res,rej)=>{
console.log("promise start====>");
res("promise end sussess!!")
})
promise.then(res=>{
console.log(res)
throw new Error("i meet some bug = =.");
},res=>{
console.log(res)
})
}
复制代码
效果图如下:
then
里面的错误,此时就用到大神们推荐的方式了。
{
//模拟一个网络请求
let promise = new Promise((res,rej)=>{
console.log("promise start====>");
res("promise end sussess!!")
})
promise.then(res=>{
console.log(res)
throw new Error("i meet some bug = =.");
}).catch(res=>{
console.log(res.message)
})
}
复制代码
效果图如下:
catch
的实现方式就是: