Heim > Fragen und Antworten > Hauptteil
比如我要做一个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里。
如果在普通模式下。可能我是习惯了不断的回调深渊了,现在接触模块化设计有点不够灵光。
伊谢尔伦2017-04-10 14:53:51
题主所遇到的问题,可以进行拆分。
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
,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, 传送门 链接描述
** 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
json
paths: { 'co': '../vendor/co/co', 'setimmediate': '../vendor/setimmediate/setimmediate' }, shim: { co: { deps: ['setimmediate'], exports: 'co' } }
这里需要一个 叫 co
模块。bower install co
co 在 浏览器中依赖 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 也许会有收获。
阿神2017-04-10 14:53:51
Promise技术可以帮到你,将模块内容定为promise对象即可回避null/undefine(想拿拿不到)和加载成功后不知道(拿到了又触发不下去)的问题