Same as in JavaScript with promises, you would use the promise ‘then’ method. The name depends on which promise library you’re using. But if you were using ReScript’s built-in one it would be Js.Promise.then_. So e.g.
Thanks @yawaramin . What I actually meant is how do I connect two promises correctly? The first promise is actually setting up the connection to MongoDB. And then there is a promise to do an actual command, like in this case the insertOne(). The second one cannot exist without the first one.
module Response = {
type t
}
module Collection = {
type t
@send external insertOne: (t, 'data) => Response.t = "insertOne"
// This should probably be Promise.t<Response.t> instead ?
}
module Db = {
type t
@send external collection: (t, string) => Collection.t = "collection"
}
module ClientInstance = {
type t
@send external db: (t, string) => Db.t = "db"
}
module Client = {
type t
@module("mongodb") @new external make: string => t = "MongoClient"
@send external connect: t => Promise.t<ClientInstance.t> = "connect"
}
let uri = "mongodb://localhost:27017"
let dbName = "rescript"
let collectionName = "documents"
type documentsType = {firstName: string, lastName: string, isLearningRescript: bool}
let testDocument = {firstName: "Alex", lastName: "Inco", isLearningRescript: true}
open Promise
let client = Client.make(uri)
client
->Client.connect
->then(res => {
let db = res->ClientInstance.db(dbName)
let collection = db->Db.collection(collectionName)
let result = collection->Collection.insertOne(testDocument)
Js.log(result)->resolve
})
->ignore
~
I can’t really call the Collection without first setting up the new MongoDBClient and then setting the dbName and collectionName first. Right?
So that would mean I would have to call the insertOne promise inside the resolved promise of the connection of the MongoClient. And that is considered incorrect based on @ryyppy documentation of rescript-promise.
I need to do two things…
Set up a connection to MongoDB (which is a promise)
Then use that connection to perform actions like find, insertOne, … on that connection. But these actions also are promises.
You can return a promise, and even the result of another promise chain within a then body. What you are not supposed to do is call Promise.resolve on a promise value, so that it forwards a Promise.t<Promise.t<...>> to the next then block.
Here’s a quick draft for your full example that show-cases the promise chaining:
module Response = {
type t
@send external toArray: t => array<'a> = "toArray"
}
module Collection = {
type t
@send external insertOne: (t, 'data) => Promise.t<Response.t> = "insertOne"
@send external find: 'query => Promise.t<Response.t> = "find"
}
module Db = {
type t
@send external collection: (t, string) => Collection.t = "collection"
}
module ClientInstance = {
type t
@send external db: (t, string) => Db.t = "db"
}
module Client = {
type t
@module("mongodb") @new external make: string => t = "MongoClient"
@send external connect: t => Promise.t<ClientInstance.t> = "connect"
@send external close: t => unit = "close"
}
let uri = "mongodb://localhost:27017"
let dbName = "rescript"
let collectionName = "documents"
type documentsType = {firstName: string, lastName: string, isLearningRescript: bool}
let testDocument = {firstName: "Alex", lastName: "Inco", isLearningRescript: true}
// Inserts some fixture test document and then finds the inserted data afterwards
let insertAndShow = (collection: Collection.t): Promise.t<array<documentsType>> => {
collection
->Collection.insertOne(testDocument)
->Promise.then(result => {
Js.log2("insertResult", result)
Collection.find(Js.Obj.empty())
})
->Promise.thenResolve(findResult => {
findResult->Response.toArray
})
}
open Promise
let client = Client.make(uri)
client
->Client.connect
->then(res => {
let db = res->ClientInstance.db(dbName)
let collection = db->Db.collection(collectionName)
insertAndShow(collection)
})
->thenResolve(result => {
Js.log2("find result", result)
})
->Promise.finally(() => {
client->Client.close
})
->ignore
Thanks @ryyppy. Now I want to also catch errors. When I add the catch block to the mix, I get the error that there is a mismatch in types between the then and the catch block.
[ReScript] [E] This has type: Promise.t<unit> (defined as Js_promise.t<unit>)
Somewhere wanted:
Promise.t<Promise.t<Result.t>> (defined as
Js_promise.t<Promise.t<Result.t>>)
} The incompatible parts:
- unit vs Promise.t<Result.t> (defined as Js_promise.t<Result.t>)
I understand I have to change the Response type, to accommodate not only the response from the then block but also from the catch block.
I tried changing the type of the insertOne as follows:
I believe you’ll need to ensure your then() and catch() functions return the same types. To keep things simple, you might like to have them each return a Promise.t<unit>.
Here’s a tweaked version of your code:
open Promise
let client = Client.make(uri)
client
->Client.connect
->then(clientInstance => {
let db = clientInstance->ClientInstance.db(dbName)
let collection = db->Db.collection(collectionName)
// Do the insert, then transform the Promise.t<Response.t> into Promise.t<unit>
collection
->Collection.insertOne({firstName: "Alex", lastName: "Incognito", isLearningRescript: true})
->then(_ => resolve())
})
->catch(e => {
switch e {
| JsError(obj) =>
switch Js.Exn.message(obj) {
| Some(msg) => Js.log("Some JS error: " ++ msg)
| None => Js.log("Some non JS error")
}
| _ => Js.log("An unknown error")
}
// Return a Promise.t<unit>
resolve()
})
->then(response => {
Js.log(response)
// Return a Promise.t<unit>
resolve()
})
->finally(() => {
client->Client.close
})
// Ignore the top level returned Promise.t<unit>
->ignore