问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

怎样用JS实现异步转同步

发布网友 发布时间:2022-05-02 21:07

我来回答

4个回答

懂视网 时间:2022-04-19 05:39

在证明这个定理之后哥德尔(对,就是证明存在不确定命题的那个)和根岑(自然演绎和相继式演算的发明人)发明了双否定变换,也叫哥德尔-根岑变换,其规则是:
alpha^{*}=(alpha
ightarrowot)
ightarrowot
(alpha
ightarroweta)^*=alpha^* 
ightarrow eta^*
注意到哥德尔-根岑变换任意命题都和原命题经典等价,但并非直觉等价(直觉逻辑本身否认
eg
egalpha
ightarrowalpha),但是按照 Gilvenko 定理,双否定命题alpha^*若在直觉逻辑体系中可证明为真,则在经典逻辑体系里alpha必为真,反之亦然。
那么按照 Curry-Howard 同构,lambdamu演算下的类型指派Gammavdash e:alpha可以经过哥德尔-根岑变换得到一个lambda演算类型指派:Gamma^*vdash e^*:alpha^*,将表达式(同构于证明过程)e变为e^*的过程就是 CPS 变换。直接照搬哥德尔-根岑变换里的类型的话,我们有如下结果:

  1. 原子 a^*=lambdakappa.kappa a

  2. 调用 (EF)^*=lambdakappa.E^*(lambda E'.F^* (lambda F'.(E'F')kappa))

  3. 抽象 (lambda x.E)^*=lambdakappa.kappa(lambda x.lambda k. (e^*)k)

  4. call/cc 算子mathrm{call/cc}=lambdakappa.kappa(lambda f.lambda k.f k k)

可以证明,E^*(lambda x.x) =_eta E,即:CPS 变换不改变语义。
当然这个版本的 CPS 是非常冗长的,市面上见到的那些都是在变换是之后直接做了eta规约,删掉大堆 Redex 的。

上面这些再一次说明了,逻辑学和编程有多么紧密的联系。 来个简明补充。

这是个简单函数计算输出:

 static int Times3(int x)
 {
 return x * 3;
 }
 Console.WriteLine(Times3(5));

(答案抄袭自http://www.cs.indiana.edu/cgi-pub/lkuper/c311/_media/cps-notes.scm
定义下面四个函数(为了保持和原答案一致,其实两个就够了)

def f(var0): 
passdef g(var0, var1): 
return passdef h(var0): 
return passdef j(var0): 
return pass

其实就是把

Fuckee 
FindFuckee(){return kula;}void Fuck(Fuckee fuckee, int count){
for(int i=0;i<count;i++)
fuckee.Fuck();}void Main(){Fuck(FindFuckee(), 100);}

可以补一补逻辑学.... @Belleve的回答太抽象了,没逻辑背景的人看不懂,
我在Quora上看到一个回答写得挺好的,里面从逻辑学的角度解释的一节或许可以作为 @Belleve答案的补充:
What is continuation-passing style in functional programming?

热心网友 时间:2022-04-19 02:47

源起

小飞是一名刚入行前端不久的新人,因为进到了某个大公司,俨然成为了学弟学妹眼中'大神',大家遇到js问题都喜欢问他,这不,此时他的qq弹出了这样一条消息

"hi,大神在吗?我有个问题想问,现在我们的代码里面有这样的东西,可是得不到正确的返回结果

1234567

function getDataByAjax () {return $.ajax(...postParam)}var data = getDataByAjax()if (data) {   console.log(data.info)}

"哦,你这里是异步调用,不能直接获得返回值,你要把if语句写到回调函数中",小飞不假思索的说到,对于一个‘专业’的fe来说,这根本不是一个问题。
“可是我希望只是改造getDataByAjax这个方法,让后面的代码成立。”
“研究这个没有意义,异步是js的精髓,同步的话会阻塞js调用,超级慢的,但是你要一再坚持的话,用async:true就好了”
“不愧是大神,我回去立刻试一试,么么哒”

两天后,她哭丧着脸登上了qq
“试了一下你的方法,但是根本行不通,哭~~”
“别急,我看看你这个postParam的参数行吗”

123456

{...   dataType: 'jsonp',async: true...}

"这是一个jsonp请求啊,老掉牙的东西了,,jsonp请求是没有办法同步的"
“我知道jsonp请求的原理是通过script标签实现的,但是,你看,script也是支持同步的呀,你看tags/attscriptasync.asp”
“额,那可能是jquery没有实现吧,哈哈”
“大神,你能帮我实现一个jsonp的同步调用方式嘛,拜托了(星星眼)”
虽然他有点奇怪jquery为什么没有实现,但是既然w3school的标准摆在那里,码两行代码又没什么,

1234567891011121314

export const loadJsonpSync = (url) => {var result; window.callback1 = (data) => (result = data)let head = window.document.getElementsByTagName('head')[0]let js = window.document.createElement('script') js.setAttribute('type', 'text/javascript') js.setAttribute('async', 'sync')  // 这句显式声明强调src不是按照异步方式调用的 js.setAttribute('src', url) head.appendChild(js)return result}

额,运行起来结果竟然是undefined!w3cshool的文档竟然也不准,还权威呢,我看也不怎么着,小飞暗自想到。

“刚才试了一下,w3school文档上写的有问题,这个异步属性根本就是错的”
“可是我刚还试过一次这个,我确认是好的呀”

12

<script src="loop50000 && put('frist').js"></script><script src="put('second').js"></script>

(有兴趣的同学可以实现以下两个js,并且加上async的标签进行尝试。)
“这个,我就搞不清楚了”,小飞讪讪的说到
对方已离线

抽象

关于这个问题,相信不只是小飞,很多人都难以解答。为什么ajax可以做到同步,但jsonp不行,推广到nodejs上,为什么readFile也可以做到同步(readFileSync),但有的库却不行。
(至于script的async选项我们暂时避而不谈,是因为现在的知识维度暂时还不够,但是不要着急,下文中会给出明确的解释)
现在,让我们以计算机科学的角度抽象这个问题:

我们是否可以将异步代码转化为同步代码呢?(ASYNCCALL => SYNCCALL)

既然是抽象问题,那么我们就可以不从工程角度/性能角度/实现语言等等等方面来看(同步比异步效率低下),每增加一个维度,复杂程度将以几何爆炸般增长下去。

首先,我们来明确一点,==在计算机科学领域==同步和异步的定义

同步(英语:Synchronization),指对在一个系统中所发生的事件(event)之间进行协调,在时间上出现一致性与统一化的现象。在系统中进行同步,也被称为及时(in time)、同步化的(synchronous、in sync)。--摘自百度百科
异步的概念和同步相对。即时间不一致,不统一

明确了这一点,我们可以借助甘特图来表示同步和异步

其中t1和t2是同步的,t1和t3是异步的。
答案就在操作系统原理的大学教材上,我们有自旋锁,信号量来解决问题,伪代码如下

1234567891011121314151617

spinLock () {// 自旋锁  fork Wait 3000 unlock() //开启一个异步线程,等待三秒后执行解锁动作  loop until unlock // 不断进行空循环直到解锁动作Put ‘unlock’} //pv原语,当信号量为假时立即执行下一步,同时将信号量置真//反之将当前执行栈挂起,置入等待唤醒队列//uv原语,将信号量置为假,并从等待唤醒队列中唤醒一个执行栈Semaphore () {  pv()  fork Wait 3000 uv()  pv()  uv()Put 'unlock'}

很好,至此都可以在操作系统原理的教材上翻到答案。于是我们在此基础上添加约束条件

仅仅依赖于js本身,我们是否可以将异步代码转化为同步代码呢?(ASYNCCALL => SYNCCALL)

论证

带着这个问题,我们翻看一下jquery的源码
src/ajax/xhr.js#L42
可以看出, ajax的同步机制本质上是由XMLHttpRequest实现的,而非js原生实现。
同样的道理,我们再翻看一下nodejs的源码
lib/fs.js#L550
从readFileSync->tryReadSync->readSync一路追下去,会追到一个c++ binding, node_file.cc#L1167

123456

if (req->IsObject()) {   ASYNC_CALL(read, req, UTF8, fd, &uvbuf, 1, pos);} else {   SYNC_CALL(read, 0, fd, &uvbuf, 1, pos)   args.GetReturnValue().Set(SYNC_RESULT);}

同步的奥妙在于c++的宏定义上,这是一种借由c++来实现的底层同步方式。
观察了这两种最广泛的异步转同步式调用,我们发现均没有采用js来实现。
似乎从现象层面上来看js无法原生支持,但是这还不够,我们探究在js语义下上面的自旋锁/信号量的特性模拟实现(我知道你们一定会嗤之以鼻,==js本身就是单线程的,只是模拟了多线程的特性== 我无比赞同这句话,所以这里用的不是实现,而是特性模拟实现),另外,由于settimeout具有fork相似的异步执行特性,所以我们用setitmeout暂时代替fork

自旋锁

1.第一个实现版本

1234567

var lock = truesetTimeout(function () {lock = false}, 5000) while(lock);console.log('unlock')

我们预期在5000ms后执行unlock语句,但是悲剧的是,整个chrome进程僵死掉了。
为了解释清楚这个问题,我们读一下阮一峰老师的event loop模型
event-loop.html
看样子咱们已经清楚的了解了event loop这个js运行顺序的本质(同步执行代码立即执行,异步代码入等待队列),那么,我们可以基于此给出js vm的调度实现(eventloop的一种实现),当然,咱们为了解释自旋锁失败只需要模拟异步操作, 同步操作,和循环就好

123456789101112131415161718192021222324

//taskQueue:任务队列//runPart:当前正在执行的任务(同步指令集)//instruct: 正在执行的指令 function eventloop (taskQueue) {while(runPart = taskQueue.shift()) {while(instruct = runPart.shift()) {const { type, act, codePart } = instructswitch(type) {case 'SYNC':         console.log(act)if (act === 'loop')           runPart.unshift({             act: 'loop',             type: 'SYNC'})breakcase 'ASYNC':         taskQueue.push(codePart)break}}}}

然后转化我们的第一个版本自旋锁

1234567891011121314151617181920

let taskQueue = [[{act: 'var lock = true', type: 'SYNC'}, //var lock = true{       act: 'setTimeout',       type: 'ASYNC',       codePart: [{act: 'lock = false', type: 'SYNC'}]}, // setTimeout(function () { lock = false }, 5000)/*{       act: 'loop',       type: 'SYNC'   },*/ // while(lock);{       act: 'console.log(\'sync\')',       type: 'SYNC'} // console.log('unlock')]]<em id="__mceDel"> </em>

测试一下,符合evnet loop的定义,然后放开注释,我们成功的让loop block住了整个执行过程,lock = false永远也没有机会执行!!!
(真实的调度机制远比这个复杂的多得多的,有兴趣的可以看看webkit~~~的jscore的实现哈)

知道了原理,我们就来手动的改进这部分代码
2.改进的代码

12345678910111213141516

var lock = truesetTimeout(function () {lock = false   console.log('unlock')}, 5000) function sleep() {var i = 5000while(i--);} var foo = () => setTimeout(function () {   sleep()lock && foo()})foo()

这个版本的改进我们对while(true);做了切块的动作,实际上这种技巧被广泛的应用到改善页面体验的方面,所以,有些人因为时序无法预知而抗拒使用settimeout这种想法是错误的!
6996528,

小测验1: 改写eventloop和taskQueue,使它支持改进后的代码

可是,如果把代码最后的foo() 变成 foo() && console.log('wait5sdo'),
我们的代码依然没有成功,why

注意看我们标红的地方,如果你完成了小测验1,就会得到和这张图一致的顺序

==同步执行的代码片段必然在异步之前。==

所以,无论从理论还是实际出发,我们都不得不承认,在js中,把异步方法改成同步方法这个命题是水月镜花

哦对了,最后还需要解释一下最开始我们埋下的坑, 为什么jsonp中的async没有生效,现在解释起来真的是相当轻松,即document.appendChild的动作是交由dom渲染线程完成的,所谓的async阻塞的是dom的解析,而非js引擎的阻塞。实际上,在async获取资源后,与js引擎的交互依旧是push taskQueue的动作,也就是我们所说的async call

推荐阅读: 关于dom解析请大家参考webkit技术内幕第九章资源加载部分

峰回路转

相信很多新潮的同学已经开始运用切了async/await语法,在下面的语法中,getAjax1和console之间的具有同步的特性

1234

async function () {var data = await getAjax1()   console.log(data)}

讲完了event loop和异步的本质,我们来重新审视一下async/await。
老天,这段代码亲手推翻了==同步执行的代码片段必然在异步之前。== 的黄金定律!
惊不惊喜,意不意外,这在我们的模型里如同三体里的质子一样的存在。我们重新审视了一遍上面的模型,实在找不到漏洞,找不到任何可以推翻的点,所以真的必须承认,async/await绝对是一个超级神奇的魔法。
到这里来看我们不得不暂时放弃前面的推论,从async/await本身来看这个问题
相信很多人都会说,async/await是CO的语法糖,CO又是generator/promise的语法糖,好的,那我们不妨去掉这层语法糖,来看看这种代码的本质, 关于CO,读的人太多了,我实在不好老生常谈,可以看看这篇文章,咱们就直接绕过去了,这里给出一个简易的实现
/5800210.html

1234567891011121314151617181920

function wrap(wait) {var iter iter = wait()const f = () => {const { value } = iter.next()   value && value.then(f)} f()} function *wait() {var p = () => new Promise(resolve => {     setTimeout(() => resolve(), 3000)})yield p() console.log('unlock1')yield p() console.log('unlock2') console.log('it\'s sync!!')}

终于,我们发现了问题的关键,如果单纯的看wait生成器(注意,不是普通的函数),是不是觉得非常眼熟。这就是我们最开始提出的spinlock伪代码!!!
这个已经被我们完完全全的否定过了,js不可能存在自旋锁,事出反常必有妖,是的,yield和*就是表演async/await魔法的妖精。
generator和yield字面上含义。Gennerator叫做生成器,yield这块ruby,python,js等各种语言界争议很大,但是大多数人对于‘让权’这个概念是认同的(以前看到过maillist上面的争论,但是具体的内容已经找不到了)

扩展阅读---ruby元编程 闭包章节yield(ruby语义下的yield)

所谓让权,是指cpu在执行时让出使用权利,操作系统的角度来看就是‘挂起’原语,在eventloop的语义下,似乎是暂存起当时正在执行的代码块(在我们的eventloop里面对应runPart),然后顺序的执行下一个程序块。
我们可以修改eventloop来实现让权机制

小测验2 修改eventloop使之支持yield原语

至此,通过修改eventloop模型固然可以解决问题,但是,这并不能被称之为魔法。

和谐共存的世界

实际上通过babel,我们可以轻松的降级使用yield,(在es5的世界使用让权的概念!!)
看似不可能的事情,现在,让我们捡起曾经论证过的
==同步执行的代码片段必然在异步之前。== 这个定理,在此基础上进行进行逆否转化

==在异步代码执行之后的代码必然不是同步执行的(异步的)。==

这是一个圈子里人尽皆知的话,但直到现在他才变得有说服力(我们绕了一个好长的圈子)
现在,让我们允许使用callback,不使用generator/yield的情况下完成一个wait generator相同的功能!!!

1234567891011121314151617181920

function wait() {const p = () => ({value: new Promise(resolve => setTimeout(() => resolve(), 3000))})let state = {next: () => {       state.next = programPartreturn p()}}function programPart() {     console.log('unlocked1')     state.next = programPart2return p()}function programPart2() {     console.log('unlocked2')     console.log('it\'s sync!!')return {value: void 0}}return state}

太棒了,我们成功的完成了generator到function的转化(虽然成本高昂),同时,这段代码本身也解释清楚了generator的本质,高阶函数,片段生成器,或者直接叫做函数生成器!这和scip上的翻译完全一致,同时拥有自己的状态(有限状态机)

推荐阅读 计算机程序的构造和解释 第一章generator部分
小测验3 实际上我们提供的解决方式存在缺陷,请从作用域角度谈谈

其实,在不知不觉中,我们已经重新发明了计算机科学中大名鼎鼎的CPS变换
Continuation-passing_style

最后的最后,容我向大家介绍一下*的CPS自动变换工具--regenerator。他在我们的基础上修正了作用域的缺陷,让generator在es5的世界里自然优雅。我们向*脱帽致敬!!egenerator

后记

同步异步 可以说是整个圈子里面最喜欢谈论的问题,但是,谈来谈去,似乎绝大多数变成了所谓的‘约定俗称’,大家意味追求新技术的同时,却并不关心新技术是如何在老技术上传承发展的,知其然而不知其所以然,人云亦云的写着似是而非的js。

==技术,不应该浮躁==

PS: 最大的功劳不是CO,也不是babel。regenerator的出现比babel早几个月,而且最初的实现是基于esprima/recast的,关于resprima/recast,国内似乎了解的并不多,其实在babel刚刚诞生之际, esprima/esprima-fb/acron 以及recast/jstransfrom/babel-generator几大族系围绕着react产生过一场激烈的斗争,或许将来的某一天,我会再从实现细节上谈一谈为什么babel笑到了最后~~~~

热心网友 时间:2022-04-19 04:05

你可以使用回调函数,promise等实现异步转同步

热心网友 时间:2022-04-19 05:40

ajax中就有设置是同步或异步
怎样用JS实现异步转同步

同步的奥妙在于c++的宏定义上,这是一种借由c++来实现的底层同步方式。观察了这两种最广泛的异步转同步式调用,我们发现均没有采用js来实现。似乎从现象层面上来看js无法原生支持,但是这还不够,我们探究在js语义下上面的自旋锁/信号量的特性模拟实现(我知道你们一定会嗤之以鼻,==js本身就是单线程的,只是模拟了多线...

微信小程序开发js如何强制同步

1.使用async/await:可以使用async/await语法来实现同步执行的效果。将异步操作包装在async函数中,通过await关键字等待异步操作完成后再继续执行后续代码。这样可以保持代码的同步性,但需要注意避免阻塞主线程,以免影响用户体验。示例:asyncfunctionfetchData(){constdata=awaitwx.request({//异步请求});//...

怎么实现JS同步、异步、延迟加载

总结: 对于支持HTML5的浏览器,实现JS的异步加载只需要在script元素中加上async属性,为了兼容老版本的IE还需加上defer属性;对于不支持HTML5的浏览器(IE可以用defer实现),可以采用以上几种方法实现。原理基本上都是向DOM中写入script或者通过eval函数执行JS代码,你可以把它放在匿名函数中执行,也可以在onload中执行,也可...

js的同步和异步

JavaScript的同步与异步实现JavaScript在编程中主要采用两种执行模式:同步和异步。同步模式意味着代码的执行顺序严格按照写入的顺序进行,遇到阻塞会暂停后续代码,直到完成。然而,这种模式的缺点在于,如果某个任务耗时较长,可能会阻塞整个程序,影响用户体验。相比之下,异步模式更为灵活。在异步模式下,当执...

js怎么将一个异步程序强制转换成同步程序

相对于之前Begin/End模式和事件模式,async/await模式让程序员得以用同步的代码结构进

js执行顺序+同步异步

按照js同步执行的顺序,函数调用会首先执行for循环,循环5次开启了5个延迟器,延时器内部的回调函数将会异步执行,会在延时1s后进入消息队列等待执行。循环了5次,所以此时i的值为5,然后同步执行console.log打印5,第一次同步执行结束,然后开始执行消息队列的异步任务,打印5个5,中间的undefined是函数...

JS中的同步和异步

1. 所有同步任务在主线程上执行,形成执行栈。2. 主线程之外存在一个“任务队列”。一旦异步任务完成,就在队列中放置一个事件。3. 当执行栈中的所有同步任务执行完毕,系统读取任务队列,将事件对应的异步任务放入执行栈,开始执行。4. 主线程不断重复步骤3,直到所有任务执行完毕。JavaScript运行机制的...

nodejs里面的fs内置模块的异步同步读取写入用法,http内置模块创建服务器...

fs:nodejs里面的内置读写模块导包:constfs=require("fs"),就是引入内置模块1.异步读取:模块名.readFile('文件路径'[,选项],function(err,data)2.异步写入:模块名.writeFile('文件路径'[,选项],function(err)3.同步读取:模块名.readFileSync("./4-1内容.txt"),(详细写法在下面)4.同步...

如何将js异步执行改成同步执行

这样你还是不能马上得到值啊 反而会卡在这里 知道取得值后才能执行下面的动作 效果不如异步的好 你可以用异步+回调函数来搞搞看

前端理解js中的同步和异步

JavaScript中常见的异步操作包括setTimeout/setInterval、AJAX、事件绑定(如on、bind、listen、addEventListener、observe)、观察者模式(例如在WebSocket中实现发布与订阅)、Promise、async/await和generator函数等。异步机制的实现依赖于任务队列和事件循环。异步任务首先被加入任务队列,等待执行。当主线程有空时...

同步转数和异步转速 同步异步转换 同步跟异步 同步异步 举例说明同步和异步 io同步和异步 ajax同步异步 同步代码和异步代码 java同步异步
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
搜索不到蓝牙设备咋办啊?我装了驱动了 安装蓝牙适配器遇到的问题!!! ...是哪位高手请指教!哦还有我忘了王梓木的老婆是哪位? ...V1 U1 W1 请问怎么对应电表接电?(我是外行) oppo ph111买哪个渠道比较好 oppo手机ph111好用吗? 哪种宠物好养?(除猫,狗,兔,鼠,水生物,冷血动物) 养什么宠物好,(不要狗和猫)要不臭的,兔子好养吗?还要小一点。 我的要求... 怎样从中华广场到一得路? 该怎么办呀?宝宝总是抓耳朵? 阮一峰的介绍 《如何变得有思想阮一峰博客文集》epub下载在线阅读全文,求百度网盘云资源 硅球,硅胶,二氧化硅的结构区别在哪? 二氧化硅和硅胶是同一物质吗? 硅球,硅胶,二氧化硅的结构区别在哪 二氧化硅和硅各自的用途(最好能区分) 硅标准溶液和二氧化硅标准溶液一样吗 微粉硅胶和二氧化硅有哪些区别 硅跟二氧化硅的物理特性比较 二氧化硅跟金属硅有什么区别 硅与二氧化硅在用途上有什么不同? 硅酸盐,二氧化硅,硅有何区别与联系,它们的用途是什么, 硅和二氧化硅的区别在那里?拜托了各位 谢谢 硅和二氧化硅的区别在那里? 两地的地理坐标怎样求它们之间的距离 中国最高龄的老人是多少岁? 为什么有些老人到了70岁过不了几年就去世了呢? 中国哪个朝代老人过了70就杀死 梦见小时候上学事、把书包丢了 中国老人7o岁还有什么保险 《如何变得有思想阮一峰博客文集》epub下载在线阅读,求百度网盘云资源 诺斯莉天气丹华泫眼霜备案编号怎么查询? Vivo Y 5 S 怎么取消屏蔽酷狗音乐? 如何屏蔽酷狗音乐小说 酷狗音乐通知栏怎么取消屏蔽 vivo x5? 如何屏蔽酷狗? 怎样屏蔽酷狗7下面的广告 怎样取消对酷狗音乐弹窗的屏蔽 请问怎么用毛豆屏蔽酷狗的弹窗广告呀? 酷狗音乐屏蔽如何解 微博实况热聊在换回了标准版怎么换回去 微博实况热聊是多长时间 学习市场营销需要学习哪些课程? 市场营销专业需要学高数吗 市场营销需要学什么? 做生意的人,市场营销有必要专门去学吗? b细胞和b淋巴细胞一样吗? B淋巴细胞的免疫功能 1:B1细胞怎样识别抗原的?2:B细胞的免疫应答需要TH2细胞辅助还需要巨噬细胞吗? 人体B细胞由哪些营养成分长的?