{"id":108,"date":"2016-04-18T15:44:30","date_gmt":"2016-04-18T19:44:30","guid":{"rendered":"https:\/\/sites.bu.edu\/perryd\/?p=108"},"modified":"2017-01-18T10:57:06","modified_gmt":"2017-01-18T15:57:06","slug":"using-javascript-promises-to-synchronize-asynchronous-methods","status":"publish","type":"post","link":"https:\/\/sites.bu.edu\/perryd\/2016\/04\/18\/using-javascript-promises-to-synchronize-asynchronous-methods\/","title":{"rendered":"Using Javascript Promises to synchronize asynchronous methods"},"content":{"rendered":"<p>The asynchronous, non-blocking Javascript runtime can be a real challenge for those of us who are used to writing in a synchronous style in languages such as Python of Java. Especially tough is when we need to do several inherently asynchronous things in a particular order&#8230;maybe a filter chain&#8230;in which the result of a preceding step is used in the next. The typical JS approach is to nest callbacks, but this leads to code that can be hard to maintain.<\/p>\n<p>The following programs illustrate the problem and work toward a solution. In each, the leading comments describe the approach and any issues that it creates. The final solution can be used as a pattern to solve general\u00a0synchronization problems in Javascript. The formatting options in the BU WordPress editor are a little limited, so you might want to cut and paste each example into your code editor for easier reading.<\/p>\n<p><b>1. The problem<\/b><\/p>\n<pre style=\"font-size: 7;\">\/*\r\nIf you are used to writing procedural code in a language like Python, Java, or C++,\r\nyou would expect this code to print step1, step2, step3, and so on. Because Javascript\r\nis non-blocking, this isn't what happens at all. The HTTP requests take time to execute,\r\nand so the JS runtime moves the call to a queue and just keeps going. Once all of the calls on the\r\nmain portion of the call stack are complete, an event loop visits each of the completed request()s \r\nin the order they completed and executes their callbacks. \r\n\r\n Starting demo\r\n Finished demo\r\n step3: UHub\r\n step2: CNN\r\n step1: KidPub\r\n\r\nSo what if we need to execute the requests in order, maybe to build up a result from each of them?\r\n*\/\r\n\r\nvar request = require('request');\r\n\r\nvar step1 = function (req1) {\r\n    request(req1, function (err, resp) {\r\n        console.log('step1: KidPub');\r\n    });\r\n};\r\n\r\nvar step2 = function (req2) {\r\n    request(req2, function (err, resp) {\r\n        console.log('step2: CNN');\r\n    });\r\n};\r\n\r\nvar step3 = function(req3) {\r\n    request(req3, function (err, resp) {\r\n        console.log('step3: UHub');\r\n    });\r\n};\r\n\r\n\r\nconsole.log('Starting demo');\r\nstep1('http:\/\/www.kidpub.com');\r\nstep2('http:\/\/www.cnn.com');\r\nstep3('http:\/\/universalhub.com');\r\nconsole.log('Finished demo');\r\n<\/pre>\n<p><b>2. Callbacks work just fine, but&#8230;<\/b><\/p>\n<pre>\/*\r\nThis is the classic way to synchronize things in Javascript using callbacks. When each\r\nrequest completes, its callback is executed. The callback is still placed in the\r\nevent queue, which is why this code prints\r\n\r\n Starting demo\r\n Finished demo\r\n step1: BU\r\n step2: CNN\r\n step3: UHub\r\n\r\nThere's nothing inherently wrong with this approach, however it can lead to what is\r\ncalled 'callback hell' or the 'pyramid of doom' when the number of synchronized items grows too large.\r\n *\/\r\nvar request = require('request');\r\nvar req1 = 'http:\/\/www.bu.edu'; var req2 = 'http:\/\/www.cnn.com'; var req3 = 'http:\/\/universalhub.com';\r\n\r\nvar step1 = function () {\r\n    request(req1, function (err, resp) {\r\n        console.log('step1: BU');\r\n        request(req2, function (err, resp) {\r\n            console.log('step2: CNN');\r\n            request(req3, function(err,resp) {\r\n                console.log('step3: UHub');\r\n            })\r\n        })\r\n    });\r\n};\r\n\r\nconsole.log('Starting demo');\r\nstep1();\r\nconsole.log('Finished demo');\r\n<\/pre>\n<p><b>3. Promises help solve the problem, but there&#8217;s a gotcha to watch out for.<\/b><\/p>\n<pre>\/*\r\nOne way to avoid callback hell is to use thenables (pronounced THEN-ables), which essentially\r\nimplement the callback in a separate function, as described in the Promise\/A+ specification. \r\nA Javascript Promise is a value that can be returned that will be filled in or completed at some\r\nfuture time. Most libraries can be wrapped with a Promise interface, and many implement Promises\r\nnatively. In the code here we're using the request-promise library, which wraps the standard HTTP\r\nrequest library with a Promise interface. The result is code that is much easier to read...an\r\nevent happens, THEN another event happes, THEN another and so on.\r\n\r\nThis might seem like a perfectly reasonable approach, chaining together\r\ncalls to external APIs in order to build up a final result.\r\nThe problem here is that a Promise is returned by the rp() call in each step...we are\r\neffectively nesting Promises. The code below appears to work, since\r\nit prints the steps on the console in the correct order. However, what's\r\nreally happening is that each rp() does NOT complete before moving on to\r\nits console.log(). If we move the console.log() statements inside the callback for\r\nthe rp() you'll see them complete out of order, as I've done in step 2. Uncomment the \r\nconsole.log() and you'll see how it starts to unravel.\r\n*\/\r\n\r\nvar rp = require('request-promise');\r\nvar Promise = require('bluebird');\r\nvar req1 = 'http:\/\/www.bu.edu'; var req2 = 'http:\/\/www.cnn.com'; var req3 = 'http:\/\/universalhub.com';\r\n\r\nfunction doCalls(message) {\r\n    return new Promise(function(resolve, reject) {\r\n        resolve(1);\r\n    })\r\n        .then(function (result) {\r\n            rp(req1, function (err, resp) {\r\n            });\r\n            console.log('step:', result, ' BU ');\r\n            return ++result;\r\n        })\r\n        .then(function (result) {\r\n                rp(req2, function (err, resp) {\r\n\/\/                    console.log('step:', result, ' CNN');\r\n                });\r\n            console.log('step:', result, ' CNN');\r\n            return ++result;\r\n            }\r\n        )\r\n        .then(function (result) {\r\n            rp(req3, function (err, resp) {\r\n            });\r\n            console.log('step:', result, ' UHub');\r\n            return ++result;\r\n        })\r\n        .then(function (result) {\r\n            console.log('Ending demo at step ', result);\r\n            return;\r\n        })\r\n}\r\ndoCalls('Starting calls')\r\n    .then(function (resolve, reject) {\r\n        console.log('Complete');\r\n    })\r\n<\/pre>\n<p><b>4. \u00a0Using Promise.resolve to order nested asynchronous calls<\/b><\/p>\n<pre>\/*\r\nHere's the final approach. In this code, each step returns a Promise to the next step, and the\r\n steps run in the expected order, since the promise isn't resolved until\r\n the rp() and its callback are complete. Here we're just manipulating a function variable,\r\n 'step', in each step but one can imagine a use case in which each of the calls \r\n would be building up a final result and perhaps storing intermediate data in a db. The \r\n result variable passed into each proceeding then is the result of the rp(), which\r\n would be the HTML page returned by the request.\r\n\r\n The advantages of this over the traditional callback method are that it results\r\n in code that's easier to read, and it also simplifies a stepwise process...each step\r\n is very cleanly about whatever that step is intended to do, and then you move\r\n on to the next step.\r\n *\/\r\n\r\nvar rp = require('request-promise');\r\nvar Promise = require('bluebird');\r\nvar req1 = 'http:\/\/www.bu.edu'; var req2 = 'http:\/\/www.cnn.com'; var req3 = 'http:\/\/universalhub.com';\r\n\r\nfunction doCalls(message) {\r\n    var step = 0;\r\n    console.log(message);\r\n    return new Promise(function(resolve, reject) {\r\n        resolve(1);\r\n    })\r\n        .then(function (result) {\r\n            return Promise.resolve(\r\n                rp(req1, function (err, resp) {\r\n                    console.log('step: ', ++step, ' BU ')\r\n                })\r\n            )\r\n        })\r\n        .then(function (result) {\r\n                return Promise.resolve(\r\n                    rp(req2, function (err, resp) {\r\n                        console.log('step: ', ++step, ' CNN');\r\n\r\n                    })\r\n                )\r\n            }\r\n        )\r\n        .then(function (result) {\r\n            return Promise.resolve(\r\n                rp(req3, function (err, resp) {\r\n                    console.log('step: ', ++step, ' UHub');\r\n                })\r\n            )\r\n        })\r\n\r\n        .then(function (result) {\r\n            console.log('Ending demo ');\r\n            return Promise.resolve(step);\r\n        })\r\n}\r\ndoCalls('Starting calls')\r\n    .then(function (steps) {\r\n            console.log('Completed in step ', steps);\r\n    });\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>The asynchronous, non-blocking Javascript runtime can be a real challenge for those of us who are used to writing in a synchronous style in languages such as Python of Java. Especially tough is when we need to do several inherently asynchronous things in a particular order&#8230;maybe a filter chain&#8230;in which the result of a preceding [&hellip;]<\/p>\n","protected":false},"author":11388,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[16,14,13],"tags":[],"_links":{"self":[{"href":"https:\/\/sites.bu.edu\/perryd\/wp-json\/wp\/v2\/posts\/108"}],"collection":[{"href":"https:\/\/sites.bu.edu\/perryd\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sites.bu.edu\/perryd\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sites.bu.edu\/perryd\/wp-json\/wp\/v2\/users\/11388"}],"replies":[{"embeddable":true,"href":"https:\/\/sites.bu.edu\/perryd\/wp-json\/wp\/v2\/comments?post=108"}],"version-history":[{"count":2,"href":"https:\/\/sites.bu.edu\/perryd\/wp-json\/wp\/v2\/posts\/108\/revisions"}],"predecessor-version":[{"id":110,"href":"https:\/\/sites.bu.edu\/perryd\/wp-json\/wp\/v2\/posts\/108\/revisions\/110"}],"wp:attachment":[{"href":"https:\/\/sites.bu.edu\/perryd\/wp-json\/wp\/v2\/media?parent=108"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sites.bu.edu\/perryd\/wp-json\/wp\/v2\/categories?post=108"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sites.bu.edu\/perryd\/wp-json\/wp\/v2\/tags?post=108"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}