mxcl / PromiseKit

Promises for Swift & ObjC.
MIT License
14.23k stars 1.46k forks source link

Clarify requirements for server-side usage #627

Closed crarau closed 6 years ago

crarau commented 7 years ago

Running this in iOS the code is getting to self.second... but while running the same code on XCode on a server side project we never pass the self.first...

   func test() {
        firstly {
            self.first()
        }.then { value in
            self.second(value: value)
        }.then { values in
            print(values)
        }
    }

    func first() -> Promise<String> {
        return Promise { fulfill, reject in
            fulfill("value")
        }
    }

    func second(value: String) -> Promise<[String]> {
        return Promise { fulfill, reject in
            fulfill([value])
        }
    }
mxcl commented 7 years ago

Xcode doesn't run on Linux… can you be more specific about your problem?

crarau commented 7 years ago

@mxcl I've created this repository to demonstrate the issue: https://github.com/crarau/promise-kit-issue

What I've meant is that I'd like to use PromiseKit using swift server side. For this I've create a project that is deployed on Ubuntu 16.04. While testing on XCode I noticed that PromiseKit doesn't work the same in this project when compared to an iOS one.

mxcl commented 7 years ago

OK, the problem is yours I am afraid, promises are asynchronous, so they need a runloop to work.

mxcl commented 7 years ago

Something like:


promise.then {
    NSRunloop.main.stop()
}

NSRunloop.main.run()

print("Done!")

Should make the project work, but really you want some kind of event-system on your server side code. To my knowledge, all of them provide one.

ChristopherCarranza commented 7 years ago

I'd love to get more info / guidance on this. I understand that a runloop is required, but wasn't aware that one wasn't available when running a server side swift project?

Promises are such an integral part of my workflow that imagining the nested closure mess i would get into without them makes me want to solve this.

I'm currently using the latest version of Kitura. I didn't quite get your example above. Would there be any way to get this to work?

crarau commented 7 years ago

to reproduce the issue I've created this repository: https://github.com/crarau/SwiftKit-ServerSide-Issue

import Foundation
import Kitura
import PromiseKit

class PromiseKitServerSide {
    func test(completion:@escaping (String)->()) {
        print("in test")

        firstly {
            return Promise.init(value: "promise")
            }.then { value in
                print("in then")
                completion(value)
            }.always {
                completion("done!");
        }
    }

}

let router = Router()
router.get("/") { request, response, next in
    PromiseKitServerSide().test(completion: { (result) in
        response.send(result)
    })
    RunLoop.main.run()
}

print("starting server")
Kitura.addHTTPServer(onPort: 8888, with: router)
Kitura.run()

going in the browser and accessing http://127.0.0.1:8888/ in the console we see:

starting server
in test

the code doesn't get into then or finally.

Adding RunLoop.main.run() doesn't change and RunLoop.main.stop() doesn't exist. Please advise, Regards

mxcl commented 7 years ago

Strictly we don't need a run loop. But the app has to enter some kind of wait state so it doesn't exit while the async stuff happens.

Also the GCD internals must be running. I don't really know what that requires.

sonny-werghis commented 7 years ago

Kitura blocks DispatchQueue.main, which is the default queue for then(). You will need to explicitly pass DispatchQueue.global to then, always, etc. As in:

      firstly {
            return Promise.init(value: "promise")
      }.then (on:DispatchQueue.global()) { value in
                print("in then")
                completion(value)
      }.always(on:DispatchQueue.global()) {
                completion("done!");
      }
mxcl commented 7 years ago

Kitura blocks DispatchQueue.main

Wow. That seems crazy.

mxcl commented 7 years ago

You can set the default queue that PromiseKit uses: https://github.com/mxcl/PromiseKit/blob/master/Sources/DispatchQueue%2BPromise.swift#L37-L52

mxcl commented 7 years ago

I don't do any server side Swift. You, the people who do, are going to have tell me how to clarify the documentation.

k1313 commented 7 years ago

You can set the default queue that PromiseKit uses: https://github.com/mxcl/PromiseKit/blob/master/Sources/DispatchQueue%2BPromise.swift#L37-L52

How we can set it?

func __PMKDefaultDispatchQueue() -> DispatchQueue {
   return DispatchQueue.main
}
 
func __PMKSetDefaultDispatchQueue(_: DispatchQueue)
{}
mxcl commented 7 years ago

Oh indeed, those are not implemented for SwiftPM. My bad. PR shouldn't be too hard though.

abl commented 6 years ago

With PMK5, you can now do:

conf.Q = (map: DispatchQueue.global(), return: DispatchQueue.global())

Here's a quick example using Kitura:

import Foundation
import Kitura
import PromiseKit
import HeliumLogger
import LoggerAPI

HeliumLogger.use(.info)

conf.Q = (map: DispatchQueue.global(), return: DispatchQueue.global())

let router = Router()
router.get("/") { _, response, next in
    Log.info("Request received")
    after(seconds: 1.0).done {
        Log.info("Sending response")
        response.send("OK")
        next()
    }
}

Log.info("Starting server")
Kitura.addHTTPServer(onPort: 8888, with: router)
Kitura.run()

This produces output like:

[2017-11-29T21:20:04.206-08:00] [INFO] [main.swift:21 promisekitura] Starting server
[2017-11-29T21:20:04.213-08:00] [INFO] [HTTPServer.swift:124 listen(on:)] Listening on port 8888
[2017-11-29T21:20:11.491-08:00] [INFO] [main.swift:13 promisekitura] Request received
[2017-11-29T21:20:12.589-08:00] [INFO] [main.swift:15 promisekitura] Sending response

You can see that roughly a second elapses between the request being received and the response being sent, so the promise chain is working, and Kitura does send the response as expected:

~
❯ http http://localhost:8888
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Length: 2
Date: Thu, 30 Nov 2017 05:23:39 GMT
Keep-Alive: timeout=60

OK

Hope this helps!

mxcl commented 6 years ago

Fixed in 5/6.