{ "id": "175994", "key": "AC-6666", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "12217", "key": "AC", "name": "Appcelerator - INBOX", "projectCategory": { "id": "10000", "description": "", "name": "Customer Service" } }, "resolution": null, "resolutiondate": null, "created": "2021-01-25T22:06:35.000+0000", "labels": [], "versions": [], "issuelinks": [], "assignee": { "name": "amukherjee", "key": "amukherjee", "displayName": "Abir Mukherjee", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2021-01-28T20:20:06.000+0000", "status": { "description": "The issue is open and ready for the assignee to start work on it.", "name": "Open", "id": "1", "statusCategory": { "id": 2, "key": "new", "colorName": "blue-gray", "name": "To Do" } }, "components": [], "description": "User interface freeze while updating the database.\r\nI think this is because we run on the UI thread now, So if we open a database connection in the same thread the UI hangs until the database connection is closed.\r\n\r\nIt's a critical issue and it affects all developers because we all use the backbone *model.save()* or *ti.database*\r\n \r\nIn this test example, you'll see the console logs but the progress bar won't update immediately.\r\n\r\n{code:javascript}\r\nconst win = Ti.UI.createWindow({\r\n\tbackgroundColor: 'white'\r\n});\r\n\r\nconst view = Ti.UI.createView({\r\n\tlayout: 'vertical',\r\n\theight: Ti.UI.SIZE,\r\n\twidth: Ti.UI.FILL\r\n});\r\n\r\nconst progressBar = Ti.UI.createProgressBar({\r\n\twidth: 250,\r\n\tmin: 0,\r\n\tmax: 1000,\r\n\tvalue: 0,\r\n\tcolor: 'blue',\r\n\tmessage: 'Inserting 0 of 1000',\r\n});\r\n\r\nconst startInsert = Ti.UI.createButton({\r\n\ttitle: \"Start Insert to DB\",\r\n\tcolor: \"#fff\",\r\n\tbackgroundColor: \"teal\",\r\n\tfont: {\r\n\t\tfontSize: '16sp'\r\n\t},\r\n\ttop: 24\r\n});\r\n\r\nstartInsert.addEventListener('click', () => {\r\n\tprogressBar.value = 0;\r\n\tprogressBar.message = `Inserting 0 of 1000`;\r\n\r\n\tconst db = Ti.Database.open('mydbInstalled');\r\n\tdb.execute('CREATE TABLE IF NOT EXISTS people (name TEXT, phone_number TEXT, city TEXT)');\r\n\tfor (let i = 0; i < 1000; i++) {\r\n\t\tdb.execute('REPLACE INTO people (name, phone_number, city) VALUES (?, ?, ?)', \"Me\", \"123456789\", \"Here\");\r\n\t\tconsole.warn(`lastInsertRowId :: ${db.lastInsertRowId}`);\r\n\t\tprogressBar.value = i;\r\n\t\tprogressBar.message = `Inserting ${i} of 1000`;\r\n\t}\r\n\tdb.close();\r\n});\r\n\r\nview.add(progressBar);\r\nview.add(startInsert);\r\nwin.add(view);\r\nwin.open();\r\n{code}\r\n", "attachment": [], "flagged": false, "summary": "UI freeze while updating the database", "creator": { "name": "ahmed.mohamed20320", "key": "ahmed.mohamed20320", "displayName": "Ahmed Mohamed", "active": true, "timeZone": "Africa/Cairo" }, "subtasks": [], "reporter": { "name": "ahmed.mohamed20320", "key": "ahmed.mohamed20320", "displayName": "Ahmed Mohamed", "active": true, "timeZone": "Africa/Cairo" }, "environment": null, "comment": { "comments": [ { "id": "458106", "author": { "name": "topener", "key": "topener", "displayName": "Rene Pot", "active": true, "timeZone": "Europe/Berlin" }, "body": "For completeness, I also commented this on TiSlack, in this specific sample you should be using transactional queries. I made a pretty simple library to do that for you; https://github.com/Topener/ti.dbutil\r\n\r\nThat said, we also offer async methods for {{execute}}, but I am not sure they are running on different threads. I pinged someone to give an answer to that. Also from your comments, it is clear your app is not as straightforward as the above sample, and according to your comments, you're also having UI block when using Async methods. ", "updateAuthor": { "name": "topener", "key": "topener", "displayName": "Rene Pot", "active": true, "timeZone": "Europe/Berlin" }, "created": "2021-01-28T13:11:16.000+0000", "updated": "2021-01-28T13:11:22.000+0000" }, { "id": "458107", "author": { "name": "gavinmatthews", "key": "gavinmatthews", "displayName": "Gavin Matthews", "active": true, "timeZone": "Europe/Dublin" }, "body": "I'm guessing [~gmathews] not me :)", "updateAuthor": { "name": "gavinmatthews", "key": "gavinmatthews", "displayName": "Gavin Matthews", "active": true, "timeZone": "Europe/Dublin" }, "created": "2021-01-28T13:11:31.000+0000", "updated": "2021-01-28T13:11:31.000+0000" }, { "id": "458109", "author": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Right. The open(), execute(), and close() methods used in the above code example are blocking. This is the correct behavior for those APIs.\r\n\r\nYou'll want to use the executeAsync() and executeAllAsync() APIs to execute SQL statements on a separate thread. (In Titanium 10.0.0, we'll be adding Promise support to those APIs as well to make them easier to use via async/await.)\r\nhttps://docs.appcelerator.com/platform/latest/#!/api/Titanium.Database.DB-method-executeAsync\r\n\r\nAn alternative solution is to use the existing blocking execute() function in a multitasking way via a JavaScript \"function generator\" and \"yield\" statements. This is JavaScript's coroutine equivalent.\r\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield\r\n", "updateAuthor": { "name": "jquick", "key": "jquick", "displayName": "Joshua Quick", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2021-01-28T20:20:06.000+0000", "updated": "2021-01-28T20:20:06.000+0000" } ], "maxResults": 4, "total": 4, "startAt": 0 } } }