javascript - requirejs模块化设计中对于异步回调函数,怎么处理较好?
大家讲道理
大家讲道理 2017-04-10 14:51:51
0
5
1026

比如我要做一个H5页面,有一些动画效果,同时带音频播放
目前我有一个app模块为主要逻辑。有一个sound模块用来初始化音频。

我是使用SoundManager 2这个音频库来初始我的音频,需要的使用方法如下。

//sound.js define(["SoundManager"],function(sm2){ var mySound; sm2.soundManager.setup({ url:'js/sm2/swf/', onready: function() { mySound = sm2.soundManager.createSound({ url: 'sound/sound1.mp3', autoLoad: true }); } }); return { mySound:mySound }; });

但是实际上 onready方法是在音频开始加载后才异步执行的。这使得我在app中获取到的mySound是undefined。

//app.js define(["sound"], function(sound) { var mySound = sound.mySound;// undefined });

于是我改成在return的时候这样写

//sound.js修改后的return return{ getMySound:function(){ return mySound; } };

然后在app中使用的时候每次都是如下:

//app.js define(["sound"], function(sound) { var mySound = sound.getMySound(); if(typeof mySound !== 'undefined'){ //doSometing()... } });

我的模块化设计思路是不是有问题?在这种回调下怎么设计要好一些?

而且如果我要在mySound加载成功后再执行一些任务,就只能到sound.js中去做了。但是我又想把类似的任务放到app.js里。

如果在普通模式下。可能我是习惯了不断的回调深渊了,现在接触模块化设计有点不够灵光。

大家讲道理
大家讲道理

光阴似箭催人老,日月如移越少年。

全部回复 (5)
伊谢尔伦

题主所遇到的问题,可以进行拆分。

requirejs 帮你解决的是模块间依赖问题,和js代码异步加载问题。

但是 requirejs 并没有帮你解决异步流程控制问题

而题主所遇到的就是典型的异步流程控制问题

使用高阶函数

这种问题有很多解决方案,其中最简单的的方法就是高阶函数(js 是一个强大的函数式编程语言)

javascript// 楼主的 资源模块 代码可以这样定义 //sound.js define(["SoundManager"],function(sm2){ return function(done) { sm2.soundManager.setup({ url:'js/sm2/swf/', onready: function() { // 返回 sound done(sm2.soundManager.createSound({ url: 'sound/sound1.mp3', autoLoad: true })); } }); } });
javascript// 楼主的 资源调用模块 代码可以这样定义 //app.js define(["sound"], function($sound) { $sound(function(sound){ // sound ready // sound.play() }); });

利用高阶函数可以犀利的解决这个问题,但是高阶函数就像 c 语言的指针。强大但是很容易让你头疼。

可以使用社区中 async 模块,可以帮你轻松管理 异步流程。它完全使用 js 高阶函数实现。

前端安装模块:bower install async

使用 Q (Promise/Deferred)

还有一种 解决方案 就是Q,Q 包含了Promise实现,和Deferred实现。

使用 deferred 可以比较优雅的处理这个问题

javascript// 楼主的 资源模块 代码可以这样定义 // 这里使用 Chrome version >= 32.0 支持 Promise //sound.js define(["SoundManager"],function(sm2){ var promise = new Promise(function(resolve, reject){ sm2.soundManager.setup({ url:'js/sm2/swf/', onready: function() { // 返回 sound try { var sound = sm2.soundManager.createSound({ url: 'sound/sound1.mp3', autoLoad: true }); // success return resolve(sound); } catch (ex) { // error return reject(new Error(ex)) } } }); }); // 返回 promise 对象 return promise; });
javascript// 楼主的 资源调用模块 代码可以这样定义 //app.js define(["sound"], function(sound) { sound.then( // success handler function(sound){ // sound ready // sound.play() }, // error handler function(err){ console.error(err); } ); });

Q可以优雅的封装我们的异步代码,管理异步中的异常,著名 jQuery 类库 1.5 之后ajax部分使用 Promise 重写,流行的前端框架AngularJS中的$q就是一个 Promise 的实现,这种模式在 java中也有所使用。

Chrome 第32个版本中内置了 Promise 类,来支持这个功能。 但是 IE 目前没有支持。

并不推荐使用浏览器自带的Promise

在前端,如果项目中,自带有 jquery 类库的话。

javascript// $.Deferred //sound.js define(["SoundManager", "jQuery"],function(sm2, $){ var deferred = $.Deferred(); sm2.soundManager.setup({ url:'js/sm2/swf/', onready: function() { // 返回 sound try { var sound = sm2.soundManager.createSound({ url: 'sound/sound1.mp3', autoLoad: true }); // success return deferred.resolve(sound); } catch (ex) { // error return deferred.reject(new Error(ex)) } } }); }); // 返回 promise 对象 return deferred; });

如果项目中没有jQuery的话,可以使用 q 模块, 还有类似的 when 模块。

bower install q只有 2.5 KB

了解更多 Promise, 传送门 链接描述

使用 generator (仅供尝鲜)

** chrome >= 39 支持, 在 nodejs > 0.11.x 支持**

javascript// 楼主的 资源模块 代码可以这样定义 //sound.js define(["SoundManager"],function(sm2){ return function(callback){ sm2.soundManager.setup({ url:'js/sm2/swf/', onready: function() { // 返回 sound try { var sound = sm2.soundManager.createSound({ url: 'sound/sound1.mp3', autoLoad: true }); // success return callback(null, sound); } catch (ex) { // error return callback(new Error(ex)); } } }); }; });

是的 还要利用 高阶函数

配置好 requirejs

jsonpaths: { 'co': '../vendor/co/co', 'setimmediate': '../vendor/setimmediate/setimmediate' }, shim: { co: { deps: ['setimmediate'], exports: 'co' } }

这里需要一个 叫co模块。
bower install coco 在 浏览器中依赖 setImmediate 模块,setImmediate 在 nodejs 中是原生

javascript// 楼主的 资源调用模块 代码可以这样定义 //app.js define(["co", "sound"], function(co, $sound) { co(function *(){ function $sound_thunkify() { return $sound;} var sound = yield $sound_thunkify(); // sound ready // sound.play() }); });

function $sound_thunkify() { return $sound;}这段为什么会这样。

tj 称这个过程叫 trunkify 翻译过来叫做块状化

你可以使用一个叫做trunkify的 模块

bower install trunkify

javascript// 楼主的 资源调用模块 代码可以这样定义 //app.js define(["co", "trunkify", "sound"], function(co, trunkify) { co(function *(){ var sound = yield trunkify($sound); // sound ready // sound.play() }); });

Generator 的出现,并不是取代 Promise 模式,他们是互补的。

如果对这种方式感兴趣的话,可以看看 朴灵的文章 -> Generator 也许会有收获。

    阿神

    Promise技术可以帮到你,将模块内容定为promise对象即可回避null/undefine(想拿拿不到)和加载成功后不知道(拿到了又触发不下去)的问题

      PHPzhong

      我也介绍一个吧
      https://github.com/caolan/async

        迷茫

        使用thenjs也可以这样的,而且比promise更加直观。

          伊谢尔伦

          q也不错 习惯angular的$q

            最新下载
            更多>
            网站特效
            网站源码
            网站素材
            前端模板
            关于我们 免责声明 Sitemap
            PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!