在JavascriptCore Framework for ObjectiveC中使用setInterval,setTimeout

Arj*_*hta 3 javascript objective-c javascriptcore ios7

我一直在尝试新的Objective C JavascriptCore框架.

这很疯狂,但它似乎没有setTimeoutsetInterval.哪个......我不明白.

  1. 我这是对的吗?
  2. 还有替代品吗?

我可以在Objective C中创建计时器,但我的大部分库都是用Javascript编写的,除此之外,看起来很奇怪没有setInterval或者setTimeout!!

我尝试了一些替代方法:

window.setInterval(function(){ /* dosomething */ }, 1000);
setInterval(function(){ /* dosomething */ }, 1000);

var interval;
interval = window.setInterval(function(){ /* dosomething */ }, 1000);
interval = setInterval(function(){ /* dosomething */ }, 1000);
Run Code Online (Sandbox Code Playgroud)

我无法监视JSVirtualMachine中甚至发生的事情.我所知道的是我的代码在setInterval被叫时停止工作.

任何帮助超级赞赏!

Jos*_*lho 9

解决旧问题的新实现.

setTimout,setInterval并且clearTimeout在JavaScriptCore的上下文中不可用.

你需要自己实现它.我已经创建了一个快速的3类来解决这个问题.通常,示例仅显示setTimeout没有使用选项的功能clearTimeout.如果您正在使用JS依赖项,那么您很可能也需要这些clearTimeoutsetInterval函数.

import Foundation
import JavaScriptCore

let timerJSSharedInstance = TimerJS()

@objc protocol TimerJSExport : JSExport {

    func setTimeout(_ callback : JSValue,_ ms : Double) -> String

    func clearTimeout(_ identifier: String)

    func setInterval(_ callback : JSValue,_ ms : Double) -> String

}

// Custom class must inherit from `NSObject`
@objc class TimerJS: NSObject, TimerJSExport {
    var timers = [String: Timer]()

    static func registerInto(jsContext: JSContext, forKeyedSubscript: String = "timerJS") {
        jsContext.setObject(timerJSSharedInstance,
                            forKeyedSubscript: forKeyedSubscript as (NSCopying & NSObjectProtocol))
        jsContext.evaluateScript(
            "function setTimeout(callback, ms) {" +
            "    return timerJS.setTimeout(callback, ms)" +
            "}" +
            "function clearTimeout(indentifier) {" +
            "    timerJS.clearTimeout(indentifier)" +
            "}" +
            "function setInterval(callback, ms) {" +
            "    return timerJS.setInterval(callback, ms)" +
            "}"
        )       
    }

    func clearTimeout(_ identifier: String) {
        let timer = timers.removeValue(forKey: identifier)

        timer?.invalidate()
    }


    func setInterval(_ callback: JSValue,_ ms: Double) -> String {
        return createTimer(callback: callback, ms: ms, repeats: true)
    }

    func setTimeout(_ callback: JSValue, _ ms: Double) -> String {
        return createTimer(callback: callback, ms: ms , repeats: false)
    }

    func createTimer(callback: JSValue, ms: Double, repeats : Bool) -> String {
        let timeInterval  = ms/1000.0

        let uuid = NSUUID().uuidString

        // make sure that we are queueing it all in the same executable queue...
        // JS calls are getting lost if the queue is not specified... that's what we believe... ;)
        DispatchQueue.main.async(execute: {
            let timer = Timer.scheduledTimer(timeInterval: timeInterval,
                                             target: self,
                                             selector: #selector(self.callJsCallback),
                                             userInfo: callback,
                                             repeats: repeats)
            self.timers[uuid] = timer
        })


        return uuid
    }

    func callJsCallback(_ timer: Timer) {
        let callback = (timer.userInfo as! JSValue)

        callback.call(withArguments: nil)
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

jsContext = JSContext()
TimerJS.registerInto(jsContext: jsContext)
Run Code Online (Sandbox Code Playgroud)

我希望有所帮助.:)


Eri*_*nge 6

我知道我在回答一个老问题,但是当我将JavaScriptCore移植到 Android 时,我遇到了这个问题。

setTimeout并且setInterval不是纯 JavaScript 的一部分。它们是 DOM 的一部分。就像document并且xmlHttpRequest在 JavaScriptCore 中不可用一样。JavaScript 被设计为单线程、同步执行环境。来自主机的调用,代码被执行,然后返回。这就是它所做的一切。主机支持发生的任何其他魔法。

所以为了在你的代码中支持它,你必须在主机端实现它。这很容易完成,如下所示:

context[@"setTimeout"] = ^(int ms, JSValue *callback) {
    [NSTimer scheduledTimerWithTimeInterval:ms/1000
        target:[NSBlockOperation blockOperationWithBlock:^{
            [callback callWithArguments:nil];
        }]
        selector:@selector(main)
        userInfo:nil
        repeats:NO
    ];
}
Run Code Online (Sandbox Code Playgroud)

不过,你需要小心。如前所述,JS 没有信号量保护或任何类似的东西。要正确实现这一点,您应该创建一个单独的执行线程:

dispatch_queue_t jsQueue = dispatch_queue_create("com.some.identifier",
    DISPATCH_QUEUE_SERIAL);
Run Code Online (Sandbox Code Playgroud)

然后在执行代码时,始终在此队列中运行它:

dispatch_async(jsQueue, ^{
    [context executeScript:myJSCode];
});
Run Code Online (Sandbox Code Playgroud)

最后,您可以将上述setTimeout函数改写如下:

context[@"setTimeout"] = ^(int ms, JSValue *callback) {
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:ms/1000
        target:[NSBlockOperation blockOperationWithBlock:^{
            dispatch_async(jsQueue, ^{
                [callback callWithArguments:nil];
            });
        }]
        selector:@selector(main)
        userInfo:nil
        repeats:NO
    ];
};
Run Code Online (Sandbox Code Playgroud)

现在它是线程安全的并且会做你期望的事情。