javascript同步是什么(javascript同步和异步的区别与实现方式)
老铁们,大家好,相信还有很多朋友对于javascript同步是什么和javascript同步和异步的区别与实现方式的相关问题不太懂,没关系,今天就由我来为大家分享分享javascript同步是什么以及javascript同步和异步的区别与实现方式的问题,文章篇幅可能偏长,希望可以帮助到大家,下面一起来看看吧!
什么是JavaScript异步
作为刚刚接触程序员小白,我们经常会遇到一些很简单的问题,但是我们不知道怎么回事,就像你要从后台调用数据,显示在前台页面,但是输出结果总是空undefined,得不到数据。这是什么原因呢?多方找资料才发现,原来是入了JS异步的“坑”。没错本篇文章我们主要和大家分享一下JS的异步操作是怎样的。
我们常常听到单线程、多线程、同步、异步这些概念,那么这些东西到底是什么呢?
那么我们先从上面那几个概念说起
单线程、多线程、同步、异步基本理解
每个正在运行的程序(即进程)至少有一个线程,被称为主线程。主线程在启动程序时被创建,用于执行main函数。
1.单线程就是只有一个主线的线程,代码从上往下顺序运行,主线程负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等等)【一个线程要做所有的事情,想想都有点累呢】
2.多线程顾名思义,就是有多个线程的程序,可以由用户自主创建。用户自主创建的若干进程相对于主线程而言就是子线程。子线程和主线程都是独立的运行单元,各自的执行互不影响,因此能够并发执行。
光听这些干巴巴的理论是不是觉得有点晕?巧了,我乍一看的时候也晕。
在找资料的过程中,我发现了别人的这么一个形象的比喻。
打个比方,单线程就是你去厨房又烧饭又烧菜,一个人来回跑;多线程就是两个人,一个单做饭,一个单做菜。
这么说,应该更好理解了吧?
而什么又是同步和异步呢?
我们用一个简单的生活例子来说明。
你打电话订酒店,问工作人员有没有房间,这时候,工作人员需要查找有没有房间才能回答你。
同步就是不挂电话一直等,直到工作人员告诉你有没有房间。
异步就是挂断电话,你去做别的事情,比如吃饭喝水,工作人员查到了信息再打电话告诉你。
那么我们的主题来了
JS的异步操作是怎样的呢?
JS的执行环境是单线程的,也就是说,程序顺序执行下来,一次只能执行一个任务,程序想要往下运行,就必须等待当前的任务执行完毕,不管当前的任务要执行多久(要是后面的程序急着跑出来可真的是等的很难受呢)。
为了解决后面程序等的难受的这个阻塞问题。JavaScript有一种异步处理模式,其实就是延时处理。
我们再来抛出例子来说明。
var getUserInfo= function(){
$.getJSON("http://www.easy-mock.com/mock/5a09868228b23066479b8379/ajaxData/getUserInfo", function(){
return data;
});
}
var data= getUserInfo();
renderUserInfo(data)getUserInfo这个函数被调用,要取后台取数据,可能要耗费很多时间,这就要让renderUserInfo一直等着,直到取出data才能运行。幸好JS有异步操作,取数据的时候,不用renderUserInfo一直等着data取出来,而是直接执行。
这么说的话,那么这两个函数到底是什么顺序执行的呐?不急,我们来调试一下:
var getUserInfo= function(){
console.log('aaa');
$.getJSON("http://www.easy-mock.com/mock/5a09868228b23066479b8379/ajaxData/getUserInfo", function(){
console.log('bbb');
return data;
});
}
var data= getUserInfo();
console.log(data);
console.log('ccc');
renderUserInfo(data);顺序执行下来的输出原以为是"aaa","bbb","ccc"吧?
然而事情并没有这么简单。我们来看一下控制台的输出:
输出的结果竟然不是顺序的。
也就是说函数执行到getJSON取数据的时候,程序并没有等它取出数据再执行下一步,而是跳过了取数据这一个阶段,直接执行输出data了,因此,data也为空。
这就是JS的异步机制了。
javascript同步和异步的区别与实现方式
javascript语言是单线程机制。所谓单线程就是按次序执行,执行完一个任务再执行下一个。
对于浏览器来说,也就是无法在渲染页面的同时执行代码。
单线程机制的优点在于实现起来较为简单,运行环境相对简单。缺点在于,如果中间有任务需要响应时间过长,经常会导致
页面加载错误或者浏览器无响应的状况。这就是所谓的“同步模式”,程序执行顺序与任务排列顺序一致。对于浏览器来说,
同步模式效率较低,耗时长的任务都应该使用异步模式;而在服务器端,异步模式则是唯一的模式,如果采用同步模式个人认为
服务器很快就会出现12306在高峰期的表现。。。。
异步模式的四种方式:
1.回调函数callback
所谓回调函数,就是将函数作为参数传到需要回调的函数内部再执行。
典型的例子就是发送ajax请求。例如:
$.ajax({
async: false,
cache: false,
dataType:'json',
url:"url",
success: function(data){
console.log('success');
},
error: function(data){
console.log('error');
}
})
当发送ajax请求后,等待回应的过程不会堵塞程序运行,耗时的操作相当于延后执行。
回调函数的优点在于简单,容易理解,但是可读性较差,耦合度较高,不易于维护。
2.事件驱动
javascript可以称之为是基于对象的语言,而基于对象的基本特征就是事件驱动(Event-Driven)。
事件驱动,指的是由鼠标和热键的动作引发的一连串的程序操作。
例如,为页面上的某个
$('#btn').onclick(function(){
console.log('click button');
});
绑定事件相当于在元素上进行监听,是否执行注册的事件代码取决于事件是否发生。
优点在于容易理解,一个元素上可以绑定多个事件,有利于实现模块化;但是缺点在于称为事件驱动的模型后,流程不清晰。
3.发布/订阅
发布订阅模式(publish-subscribe pattern)又称为观察者模式(Observer pattern)。
该模式中,有两类对象:观察者和目标对象。目标对象中存在着一份观察者的列表,当目标对象
的状态发生改变时,主动通知观察者,从而建立一种发布/订阅的关系。
jquery有相关的插件,在这不是重点不细说了。。。。回头写个实现贴上来
4.promise模式
promise对象是CommonJS工作组提供的一种规范,用于异步编程的统一接口。
promise对象通常实现一种then的方法,用来在注册状态发生改变时作为对应的回调函数。
promise模式在任何时刻都处于以下三种状态之一:未完成(unfulfilled)、已完成(resolved)和拒绝(rejected)。以CommonJS
Promise/A
标准为例,promise对象上的then方法负责添加针对已完成和拒绝状态下的处理函数。then方法会返回另一个promise对象,以便于形成promise管道,这种返回promise对象的方式能够支持开发人员把异步操作串联起来,如then(resolvedHandler,
rejectedHandler);。resolvedHandler
回调函数在promise对象进入完成状态时会触发,并传递结果;rejectedHandler函数会在拒绝状态下调用。
Jquery在1.5的版本中引入了一个新的概念叫Deferred,就是CommonJS promise A标准的一种衍生。可以在jQuery中创建
$.Deferref的对象。同时也对发送ajax请求以及数据类型有了新的修改,参考JQuery API。
除了以上四种,javascript中还可以利用各种函数模拟异步方式,更有诡异的诸如用同步调用异步的case
只能用team里同事形容java和javascript的一句话作为结尾:
“写java像在高速路上开车,写javascript像在草原上开车”-------------以此来形容javascript这种无类型的语言有多自由
but,如果草原上都是坑。
如何实现 javascript “同步”调用 app 代码
在 App混合开发中,app层向 js层提供接口有两种方式,一种是同步接口,一种一异步接口(不清楚什么是同步的请看这里的讨论)。为了保证 web流畅,大部分时候,我们应该使用异步接口,但是某些情况下,我们可能更需要同步接口。同步接口的好处在于,首先 js可以通过返回值得到执行结果;其次,在混合式开发中,app层导出的某些 api按照语义就应该是同步的,否则会很奇怪——一个可能在 for循环中使用的,执行非常快的接口,比如读写某个配置项,设计成异步会很奇怪。
那么如何向 js层导出同步接口呢?
我们知道,在 Android框架中,通过 WebView.addJavascriptInterface()这个函数,可以将 java接口导出到 js层,并且这样导出的接口是同步接口。但是在 iOS的 Cocoa框架中,想导出同步接口却不容易,究其原因,是因为 UIWebView和 WKWebView没有 addJavascriptInterface这样的功能。同时,Android这个功能爆出过安全漏洞,那么,我们有没有别的方式实现同步调用呢?我们以 iOS UIWebView为例提供一种实现,WKWebView和 Android也可以参考。
为了找到问题的关键,我们看一下 iOS中实现 js调用 app的通行方法:
首先,自定义 UIWebViewDelegate,在函数 shouldStartLoadWithRequest:navigationType:中拦截请求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-(BOOL) webView:(UIWebView* _Nonnull)webView
shouldStartLoadWithRequest:(NSURLRequest* _Nonnull)request
navigationType:(UIWebViewNavigationType)navigationType{
if([request.HTTPMethod compare:@"GET" options:NSCaseInsensitiveSearch]!= NSOrderedSame){
//不处理非 get请求
return YES;
}
NSURL* url= request.URL;
if([url.scheme isEqualToString:@'YourCustomProtocol']){
return [self onMyRequest:request];
}
return YES;
}
这种做法实质上就是将函数调用命令转化为 url,通过请求的方式通知 app层,其中 onMyRequest:是自定义的 request响应函数。为了发送请求,js层要建立一个隐藏的 iframe元素,每次发送请求时修改 iframe元素的 src属性,app即可拦截到相应请求。
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* js向 native传递消息
*@method js_sendMessageToNativeAsync
*@memberof JSToNativeIOSPolyfill
*@public
*@param str{String}消息字符串,由 HybridMessage转换而来
*/
JSToNativeIOSPolyfill.prototype.js_sendMessageToNativeAsync= function(str){
if(!this.ifr_){
this._prepareIfr();
}
this.ifr_.src='YourCustomProtocol://__message_send__?msg='+ encodeURIComponent(str);}
当 app执行完 js调用的功能,执行结果无法直接返回,为了返回结果,普遍采用回调函数方式——js层记录一个 callback,app通过 UIWebView的 stringByEvaluatingJavaScriptFromString函数调用这个 callback(类似 jsonp的机制)。
注意,这样封装的接口,天然是异步接口。因为 js_sendMessageToNativeAsync这个函数会立即返回,不会等到执行结果发回来。
所以,我们要想办法把 js代码“阻塞”住。
请回忆一下,js中是用什么方法能把 UI线程代码“阻塞”住,同时又不跑满 CPU?
1
2
3
4
var async= false;
var url='http://baidu.com';
var method='GET';<br>var req= new XMLHttpRequest();<br>
req.open(method, url, async);<br>req.send(null);
“同步”ajax(其实没这个词,ajax内涵异步的意思)可以!在 baidu的响应没返回之前,这段代码会一直阻塞。一般来说同步请求是不允许使用的,有导致 UI卡顿的风险。但是在这里因为我们并不会真的去远端请求内容,所以不妨一用。
至此实现方式已经比较清楚了,梳理一下思路:
使用同步 XMLHttpRequest配合特殊构造的 URL通知 app层。
app层拦截请求执行功能,将结果作为 Response返回。
XMLHttpRequest.send()返回,通过 status和 responseText得到结果。
那么,如何拦截请求呢?大家知道,UIWebViewDelegate是不会拦截 XMLHttpRequest请求的,但是 iOS至少给了我们两个位置拦截这类请求——NSURLCache和 NSURLProtocol。
一、NSURLCache是 iOS中用来实现自定义缓存的类,当你创建了自定义的 NSURLCache子类对象,并将其设置为全局缓存管理器,所有的请求都会先到这里检查有无缓存(如果你没禁掉缓存的话)。我们可以借助这个性质拦截到接口调用请求,执行并返回数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-(NSCachedURLResponse*) cachedResponseForRequest:(NSURLRequest*)request{
if([request.HTTPMethod compare:@"GET" options:NSCaseInsensitiveSearch]!= NSOrderedSame){
//只对 get请求做自定义处理
return [super cachedResponseForRequest:request];
}
NSURL* url= request.URL;
NSString* path= url.path;
NSString* query= url.query;
if(path== nil|| query== nil){
return [super cachedResponseForRequest:request];
}
LOGF(@"url=%@, path=%@, query=%@", url, path, query);
if([path isEqualToString:@"__env_get__"]){
//读环境变量
return [self getEnvValueByURL:url];//*
} else if([path isEqualToString:@"__env_set__"]){
//写环境变量
return [self setEnvValueByURL:url];
}
return [super cachedResponseForRequest:request];
}
注意注释有*号的一行,即是执行 app接口,返回结果。这里的结果是一个 NSCachedResponse对象,就不赘述了。
好了,本文到此结束,如果可以帮助到大家,还望关注本站哦!