{"id":1722,"date":"2015-12-01T12:30:10","date_gmt":"2015-12-01T07:00:10","guid":{"rendered":"http:\/\/ramkulkarni.com\/blog\/?p=1722"},"modified":"2015-12-01T12:30:10","modified_gmt":"2015-12-01T07:00:10","slug":"using-javascript-promises-with-cordova","status":"publish","type":"post","link":"http:\/\/ramkulkarni.com\/blog\/using-javascript-promises-with-cordova\/","title":{"rendered":"Using JavaScript Promises with Cordova"},"content":{"rendered":"<h2>JavaScript Promises<\/h2>\n<p>JavaScript Promises could make asynchronous programming a bit easier. Most of the APIs of Cordova are asynchronous. So when you want to call one asynchronous API after the other, you have to nest API in the callback functions of the previous API. Many\u00a0APIs also take error handler as one of the parameter to API function. At some point the whole code could become very difficult to read and maintain.<\/p>\n<p>JS Promises \u00a0could make this a bit simpler. Promise is an object, which takes a function callback with two arguments, resolve and reject.\u00a0Promise represent a value that would be resolved sometime in future, it is is not already resolved or rejected. The promise can be rejected explicitly or when any JavaScript exception is thrown. See\u00a0<a href=\"https:\/\/promisesaplus.com\/\" target=\"_blank\">https:\/\/promisesaplus.com\/<\/a> and\u00a0<a href=\"https:\/\/developer.mozilla.org\/en\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Promise\" target=\"_blank\">Promises on MDN<\/a>\u00a0for details on JS Promises. Advantages of Promises is realized when you need to chain multiple asynchronous calls. Promises can also be useful if you want to guarantee then a piece of code is executed only once.\u00a0Promises are executed only once.<!--more--><\/p>\n<p>Before we see how Promises can simplify usage of Cordova APIs, let&#8217;s see a simple example of Promises. Let&#8217;s say there are three asynchronous functions and they need to be called one after the other.<!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #557799;\">&lt;!DOCTYPE html&gt;<\/span>\n<span style=\"color: #007700;\">&lt;html&gt;<\/span>\n\t<span style=\"color: #007700;\">&lt;head&gt;<\/span>\n\t\t<span style=\"color: #007700;\">&lt;title&gt;<\/span>JavaScript Promises Test<span style=\"color: #007700;\">&lt;\/title&gt;<\/span>\n\t<span style=\"color: #007700;\">&lt;\/head&gt;<\/span>\n\t<span style=\"color: #007700;\">&lt;body&gt;<\/span>\n\t\t<span style=\"color: #007700;\">&lt;script&gt;<\/span>\n\t\t\t<span style=\"color: #008800; font-weight: bold;\">function<\/span> asyncFunc1(throwError, successCallback, errorCallback) {\n\t\t\t\t<span style=\"color: #007020;\">window<\/span>.setTimeout(<span style=\"color: #008800; font-weight: bold;\">function<\/span>(){\n\t\t\t\t\tconsole.log(<span style=\"background-color: #fff0f0;\">\"Executed asyncFunc1\"<\/span>);\n\t\t\t\t\t<span style=\"color: #008800; font-weight: bold;\">if<\/span> (throwError)\n\t\t\t\t\t\terrorCallback({error<span style=\"color: #333333;\">:<\/span><span style=\"background-color: #fff0f0;\">'Error from asyncFunc1'<\/span>});\n\t\t\t\t\t<span style=\"color: #008800; font-weight: bold;\">else<\/span>\n\t\t\t\t\t\tsuccessCallback({retValue <span style=\"color: #333333;\">:<\/span> <span style=\"background-color: #fff0f0;\">'asyncFunc1 return value'<\/span>});\n\t\t\t\t}, <span style=\"color: #0000dd; font-weight: bold;\">1000<\/span>);\n\t\t\t}\n\n\t\t\t<span style=\"color: #008800; font-weight: bold;\">function<\/span> asyncFunc2(throwError, successCallback, errorCallback) {\n\t\t\t\t<span style=\"color: #007020;\">window<\/span>.setTimeout(<span style=\"color: #008800; font-weight: bold;\">function<\/span>(){\n\t\t\t\t\tconsole.log(<span style=\"background-color: #fff0f0;\">\"Executed asyncFunc2\"<\/span>);\n\t\t\t\t\t<span style=\"color: #008800; font-weight: bold;\">if<\/span> (throwError)\n\t\t\t\t\t\terrorCallback({error<span style=\"color: #333333;\">:<\/span><span style=\"background-color: #fff0f0;\">'Error from asyncFunc2'<\/span>});\n\t\t\t\t\t<span style=\"color: #008800; font-weight: bold;\">else<\/span>\n\t\t\t\t\t\tsuccessCallback({retValue <span style=\"color: #333333;\">:<\/span> <span style=\"background-color: #fff0f0;\">'asyncFunc2 return value'<\/span>});\n\t\t\t\t}, <span style=\"color: #0000dd; font-weight: bold;\">1000<\/span>);\n\t\t\t}\n\n\t\t\t<span style=\"color: #008800; font-weight: bold;\">function<\/span> asyncFunc3(throwError, successCallback, errorCallback) {\n\t\t\t\t<span style=\"color: #007020;\">window<\/span>.setTimeout(<span style=\"color: #008800; font-weight: bold;\">function<\/span>(){\n\t\t\t\t\tconsole.log(<span style=\"background-color: #fff0f0;\">\"Executed asyncFunc3\"<\/span>);\n\t\t\t\t\t<span style=\"color: #008800; font-weight: bold;\">if<\/span> (throwError)\n\t\t\t\t\t\terrorCallback({error<span style=\"color: #333333;\">:<\/span><span style=\"background-color: #fff0f0;\">'Error from asyncFunc3'<\/span>});\n\t\t\t\t\t<span style=\"color: #008800; font-weight: bold;\">else<\/span>\n\t\t\t\t\t\tsuccessCallback({retValue <span style=\"color: #333333;\">:<\/span> <span style=\"background-color: #fff0f0;\">'asyncFunc3 return value'<\/span>});\n\t\t\t\t}, <span style=\"color: #0000dd; font-weight: bold;\">1000<\/span>);\n\t\t\t}\n\n\t\t\t<span style=\"color: #008800; font-weight: bold;\">function<\/span> genericErrorHandler (err) {\n\t\t\t\tconsole.log(<span style=\"background-color: #fff0f0;\">\"Error : \"<\/span>, err);\n\t\t\t}\n\t\t<span style=\"color: #007700;\">&lt;\/script&gt;<\/span>\n\n\t<span style=\"color: #007700;\">&lt;\/body&gt;<\/span>\n<span style=\"color: #007700;\">&lt;\/html&gt;<\/span><\/pre>\n<\/div>\n<p>Without using Promises, you would call the functions as follows &#8211;<!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;\">\n<pre style=\"margin: 0; line-height: 125%;\">asyncFunc1(<span style=\"color: #008800; font-weight: bold;\">false<\/span>, <span style=\"color: #008800; font-weight: bold;\">function<\/span>(ret1){\n \tasyncFunc2(<span style=\"color: #008800; font-weight: bold;\">false<\/span>, <span style=\"color: #008800; font-weight: bold;\">function<\/span>(ret2){\n \t\tasyncFunc3(<span style=\"color: #008800; font-weight: bold;\">false<\/span>, <span style=\"color: #008800; font-weight: bold;\">function<\/span>(ret3){\n \t\t\tconsole.log(ret3);\n \t\t}, genericErrorHandler);\n \t}, genericErrorHandler);\n}, genericErrorHandler);\n<\/pre>\n<\/div>\n<p>Notice nesting of function calls in the callback of previous call. This may not look too complicated in this example, but if you have large block of code in callback functions then it might become difficult to read and understand the code.<\/p>\n<p>Now let&#8217;s see how we can use JS Promises to call the same three async functions.\u00a0We will use <a href=\"https:\/\/www.promisejs.org\/\" target=\"_blank\">PromiseJS<\/a> polyfill. Include following script &#8211;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #007700;\">&lt;script <\/span><span style=\"color: #0000cc;\">src=<\/span><span style=\"background-color: #fff0f0;\">\"https:\/\/www.promisejs.org\/polyfills\/promise-7.0.4.min.js\"<\/span><span style=\"color: #007700;\">&gt;&lt;\/script&gt;<\/span>\n<\/pre>\n<\/div>\n<p>Here is the code to call async functions sequentially using JS Promises &#8211;<br \/>\n<!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #008800; font-weight: bold;\">new<\/span> Promise(<span style=\"color: #008800; font-weight: bold;\">function<\/span>(resolve, reject){\n\tasyncFunc1(<span style=\"color: #008800; font-weight: bold;\">false<\/span>, resolve, reject);\n}).then (<span style=\"color: #008800; font-weight: bold;\">function<\/span>(ret1) {\n\t<span style=\"color: #008800; font-weight: bold;\">return<\/span> <span style=\"color: #008800; font-weight: bold;\">new<\/span> Promise(<span style=\"color: #008800; font-weight: bold;\">function<\/span>(resolve,reject){\n\t\tasyncFunc2(<span style=\"color: #008800; font-weight: bold;\">false<\/span>, resolve, reject);\n\t});\n}).then (<span style=\"color: #008800; font-weight: bold;\">function<\/span>(ret2){\n\t<span style=\"color: #008800; font-weight: bold;\">return<\/span> <span style=\"color: #008800; font-weight: bold;\">new<\/span> Promise(<span style=\"color: #008800; font-weight: bold;\">function<\/span>(resolve,reject){\n\t\tasyncFunc3(<span style=\"color: #008800; font-weight: bold;\">false<\/span>, resolve, reject);\n\t});\n}).then(<span style=\"color: #008800; font-weight: bold;\">function<\/span>(ret3) {\n\tconsole.log(ret3);\n}, genericErrorHandler);\n<\/pre>\n<\/div>\n<p>Notice that the error handler is handled in the last &#8216;then&#8217; call. Any error occurred in any of the chained promises would be caught by this error handler. A couple of other notes about Promises &#8211;<\/p>\n<ol>\n<li>As mentioned earlier, Promise is executed only once. However, you can call &#8216;then&#8217; method multiple times. If the promise is already executed then appropriate callback function passed in the &#8216;then&#8217; would be called &#8211; i.e. if promise was executed with success then resolve callback would be called and if error had occurred then reject callback method would be called. &#8216;then&#8217; takes two arguments &#8211; the first one is success callback and the second one is error callback.<\/li>\n<li>If you return a value from &#8216;then&#8217; method that is a Promise object then the next &#8216;then&#8217; method is called only after the Promise is executed. If the value returned from &#8216;then&#8217; is not a Promise, the next &#8216;then&#8217; method is called immediately<\/li>\n<li>Value passed to resolved callback method (first argument to &#8216;then&#8217; method) is either the value with which earlier Promise was resolved or value returned directly from the previous Promise\/&#8217;then&#8217; method<\/li>\n<\/ol>\n<h2>Cordova Example<\/h2>\n<p>Let&#8217;s take an example from Cordova APIs. Let&#8217;s say we want to take a picture using camera, and copy\u00a0it to applications data directory. Following async calls are involved in this example<\/p>\n<ol>\n<li>Call Camera API to take picture. It will return path of the image file (which would be in temp cache folder of the application)<\/li>\n<li>Resolve the path to get FileEntry object<\/li>\n<li>Resolve path to application&#8217;s data directory<\/li>\n<li>Copy the image file to application&#8217;s data directory<\/li>\n<\/ol>\n<p>Here is the code snippet without using Promises &#8211;<\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #888888;\">\/\/Get picture from Camera and save to application's data folder.<\/span>\n<span style=\"color: #888888;\">\/\/success callback returns path of the copied image file <\/span>\n<span style=\"color: #008800; font-weight: bold;\">function<\/span> getPictureWithoutPromises(successCallback, errorCallback) {\n\t<span style=\"color: #888888;\">\/\/Call Camera API to get Picture<\/span>\n\tnavigator.camera.getPicture(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (imagePath) {\n\t\t<span style=\"color: #888888;\">\/\/resolve the image path, to get FileEntry object<\/span>\n\t\t<span style=\"color: #007020;\">window<\/span>.resolveLocalFileSystemURL(imagePath, <span style=\"color: #008800; font-weight: bold;\">function<\/span> (imageFileEntry) {\n\t\t\t<span style=\"color: #888888;\">\/\/now get path to applications data folder<\/span>\n\t\t\t<span style=\"color: #007020;\">window<\/span>.resolveLocalFileSystemURL(cordova.file.dataDirectory, <span style=\"color: #008800; font-weight: bold;\">function<\/span> (appDataDirEntry) {\n\t\t\t\t<span style=\"color: #888888;\">\/\/copy image file to the data directory<\/span>\n\t\t\t\timageFileEntry.copyTo(appDataDirEntry, <span style=\"color: #008800; font-weight: bold;\">null<\/span>, <span style=\"color: #008800; font-weight: bold;\">function<\/span> (newImageFileEntry) {\n\t\t\t\t\t<span style=\"color: #888888;\">\/\/call success callback handler<\/span>\n\t\t\t\t\tsuccessCallback(newImageFileEntry);\n\t\t\t\t}, errorCallback);\n\t\t\t}, errorCallback);\n\t\t}, errorCallback);\n\t}, errorCallback);\n}\n<\/pre>\n<\/div>\n<p>And this is how you would call this function &#8211;<\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;\">\n<pre style=\"margin: 0; line-height: 125%;\">getPictureWithoutPromises(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (imageFileEntry) {\n\t<span style=\"color: #888888;\">\/\/set src of an &lt;img&gt; tag, assuming we got reference to &lt;img&gt; DOM element before<\/span>\n\timage.src <span style=\"color: #333333;\">=<\/span> imageFileEntry.toURL();\n}, <span style=\"color: #008800; font-weight: bold;\">function<\/span> (err) {\n\tconsole.log(<span style=\"background-color: #fff0f0;\">\"Error : \"<\/span>, err);\n});<\/pre>\n<\/div>\n<p>The same could be written using Promises as follows &#8211;<br \/>\n<!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #888888;\">\/\/Get picture from Camera and save to application's data folder.<\/span>\n<span style=\"color: #888888;\">\/\/Returns a Promise, whose resolve method returns path to the copied image file <\/span>\n<span style=\"color: #008800; font-weight: bold;\">function<\/span> getPictureWithPromises() {\n\t<span style=\"color: #008800; font-weight: bold;\">var<\/span> sourceImageFileEntry;\n\n\t<span style=\"color: #888888;\">\/\/return a Promise<\/span>\n\t<span style=\"color: #008800; font-weight: bold;\">return<\/span> <span style=\"color: #008800; font-weight: bold;\">new<\/span> Promise(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (returnResolve, returnReject) {\n\t\t<span style=\"color: #008800; font-weight: bold;\">new<\/span> Promise(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (resolve, reject) {\n\t\t\t<span style=\"color: #888888;\">\/\/Call Camera API to get Picture\t\t\t\t<\/span>\n\t\t\tnavigator.camera.getPicture(resolve, reject);\n\t\t}).then(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (imagePath) {\n\t\t\t<span style=\"color: #008800; font-weight: bold;\">return<\/span> <span style=\"color: #008800; font-weight: bold;\">new<\/span> Promise(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (resolve, reject) {\n\t\t\t\t<span style=\"color: #888888;\">\/\/resolve the image path, to get FileEntry object<\/span>\n\t\t\t\t<span style=\"color: #007020;\">window<\/span>.resolveLocalFileSystemURL(imagePath, resolve, reject);\n\t\t\t});\n\t\t}).then(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (imageFileEntry) {\n\t\t\t<span style=\"color: #888888;\">\/\/save this file entry in a local variable, this will be used when copying the file\t<\/span>\n\t\t\tsourceImageFileEntry <span style=\"color: #333333;\">=<\/span> imageFileEntry;\n\t\t\t<span style=\"color: #008800; font-weight: bold;\">return<\/span> <span style=\"color: #008800; font-weight: bold;\">new<\/span> Promise(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (resolve, reject) {\n\t\t\t\t<span style=\"color: #888888;\">\/\/now get path to applications data folder<\/span>\n\t\t\t\t<span style=\"color: #007020;\">window<\/span>.resolveLocalFileSystemURL(cordova.file.dataDirectory, resolve, reject);\n\t\t\t});\n\t\t}).then(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (appDataDirEntry) {\n\t\t\t<span style=\"color: #008800; font-weight: bold;\">return<\/span> <span style=\"color: #008800; font-weight: bold;\">new<\/span> Promise(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (resolve, reject) {\n\t\t\t\t<span style=\"color: #888888;\">\/\/copy image file to the data directory<\/span>\n\t\t\t\tsourceImageFileEntry.copyTo(appDataDirEntry, <span style=\"color: #008800; font-weight: bold;\">null<\/span>, resolve, reject);\n\t\t\t});\n\t\t}).then(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (newImageFileEntry) {\n\t\t\t\t<span style=\"color: #888888;\">\/\/resolve the first Promise (which is returned form this method)\t <\/span>\n\t\t\t\treturnResolve(newImageFileEntry)\n\t\t});\n\t});\n}\n<\/pre>\n<\/div>\n<p>And can be called as follows &#8211;<\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;\">\n<pre style=\"margin: 0; line-height: 125%;\">getPictureWithPromises().then(<span style=\"color: #008800; font-weight: bold;\">function<\/span> (imageFileEntry) {\n\timage.src <span style=\"color: #333333;\">=<\/span> imageFileEntry.toURL();\n}, <span style=\"color: #008800; font-weight: bold;\">function<\/span> (err) {\n\tconsole.log(<span style=\"background-color: #fff0f0;\">\"Error : \"<\/span>, err);\n});<\/pre>\n<\/div>\n<p>Promises do not completely remove nested callbacks, but you can limit them to one or max two level.<\/p>\n<p>-Ram Kulkarni<\/p>\n","protected":false},"excerpt":{"rendered":"<p>JavaScript Promises JavaScript Promises could make asynchronous programming a bit easier. Most of the APIs of Cordova are asynchronous. So when you want to call one asynchronous API after the other, you have to nest API in the callback functions of the previous API. Many\u00a0APIs also take error handler as one of the parameter to &hellip; <\/p>\n<p class=\"link-more\"><a href=\"http:\/\/ramkulkarni.com\/blog\/using-javascript-promises-with-cordova\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Using JavaScript Promises with Cordova&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"Using JavaScript Promises with Cordova","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[120,20,17,1],"tags":[121,5,13,122],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p2g9O8-rM","jetpack-related-posts":[],"_links":{"self":[{"href":"http:\/\/ramkulkarni.com\/blog\/wp-json\/wp\/v2\/posts\/1722"}],"collection":[{"href":"http:\/\/ramkulkarni.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/ramkulkarni.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/ramkulkarni.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/ramkulkarni.com\/blog\/wp-json\/wp\/v2\/comments?post=1722"}],"version-history":[{"count":0,"href":"http:\/\/ramkulkarni.com\/blog\/wp-json\/wp\/v2\/posts\/1722\/revisions"}],"wp:attachment":[{"href":"http:\/\/ramkulkarni.com\/blog\/wp-json\/wp\/v2\/media?parent=1722"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/ramkulkarni.com\/blog\/wp-json\/wp\/v2\/categories?post=1722"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/ramkulkarni.com\/blog\/wp-json\/wp\/v2\/tags?post=1722"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}