Today we will talk about Promise
. In javascript we have worked with it a lot, for example when we manipulated the API or when we wanted to create an asynchronous handler. Promise
is a concept that is neither easy to understand nor difficult to understand. But depending on our level of understanding it will give us more choice in handling situations in javascript. Today let’s learn about a way to manage multiple Promise
or asynchronous handling. In this article we will combine the use of class
and Promise
.
class
and Promise
in javascript
1.1 Class
The class
concept introduced in the standard ES6 of javascript is based on the inheritance of prototypal inheritance
in javascript. Basically prototypal inheritance
refers to the ability to access properties of object A from another object B (we will look at this in another article).
In fact, the class
is a special function and when we call that function with the keyword new
we will get an instance of an object that the class
has defined. And when initialized with the keyword new
, the constructor
method in that class will be called (here we can take the input parameter and use it in the class).
In today’s article we will use class
to store variables used to manage multiple Promise
.
1.2 Promise
The Promise
concept is too familiar to us. Promise
represents an asynchronous process (processor needs to wait a large / small amount of time to complete).
When initiating a Promise
it will need to be input with a callback function. This function will take two input parameters, resolve
and reject
. These two parameters will allow us to decide Promise
that we will successfully initialize returns data ( resolve
) or will fail and return an error code ( reject
).
A Promise that is launched can have one of the three states, pending
, fulfilled
, rejected
. pending
is the finished pending
state, which is the initial state of a Promise
. fulfilled
is the successful processing state, it indicates that the Promise
processing succeeded (using resolve
to change the Promise
back to this state). Finally, rejected
is the failed handling state, which indicates that the Promise
treatment was failed (using reject
to convert the Promise
back to this state).
A Promise
when running will only be successful or failed (except for the initial pending
state). After successful or failed processing it will return the corresponding data on success and error code on failure. We can use .then
to manipulate the corresponding success data and .catch
to manipulate the returned error code data.
Another way we can easily return a successful or failed Promise
with the corresponding success or failure data or error code. Promise.prototype.resolve
and Promise.prototype.reject
will return a successful Promise
instance or a failed Promise
instance in turn. It helps us to be more flexible in how to handle the situation, not depending too much on the handling.
Next, let’s try to combine the two above concepts to handle the problem of managing multiple Promise
.
2. Combine class
and Promise
Our goal first will be to manage multiple asynchronous handlers that allow error handling for these multiple threads. When an error handler occurs we will pause the subsequent error handlers and run another intermediate handler (for debugging) and until the handler is successful we will start running the other handlers again. error handling was paused earlier and ended a thread.
The real situation where we can apply is the handling of refresh session authen upon expiration. Next to the code we will manipulate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | <span class="token comment">// Chúng ta sẽ khai báo một điều kiện để handle lỗi</span> <span class="token comment">// Dưới đây là mã lỗi mong muốn khi xử lý bị lỗi của chúng ra trả về</span> <span class="token keyword">const</span> <span class="token constant">DESIRED_ERROR_CODE</span> <span class="token operator">=</span> <span class="token number">1000</span> <span class="token comment">// Chúng ta sẽ fake 1 xử lý bất đồng bộ</span> <span class="token comment">// Trả về một thể hiện Promise và điều khiển </span> <span class="token comment">// thành công hoặc thất bại bằng tham số "needReject"</span> <span class="token comment">// Khi lỗi chúng ta sẽ trả mã lỗi mà chúng ta mong muốn ở trên</span> <span class="token keyword">function</span> <span class="token function">doSomething</span> <span class="token punctuation">(</span> <span class="token parameter">config <span class="token punctuation">,</span> needReject</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">resolve <span class="token punctuation">,</span> reject</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">setTimeout</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">'run "doSomething async"'</span> <span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> needReject <span class="token punctuation">)</span> <span class="token function">resolve</span> <span class="token punctuation">(</span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> needReject <span class="token punctuation">)</span> <span class="token function">reject</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> code <span class="token operator">:</span> <span class="token constant">DESIRED_ERROR_CODE</span> <span class="token punctuation">,</span> message <span class="token operator">:</span> <span class="token string">'Some thing went wrong'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token number">2000</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// Tiếp đến tạo một class "AnyService"</span> <span class="token comment">// dùng để quản lý và handle lỗi.</span> <span class="token comment">// "isProcessing" dùng để làm tín hiệu là có 1 xử lý lỗi</span> <span class="token comment">// trước đó và các xử lý lỗi phía sau sẽ cần tạm dừng và chạy lại sau</span> <span class="token comment">// "queue" dùng để chứa những xử lý lỗi đang được tạm dừng</span> <span class="token comment">// và sẽ chạy lại khi xử lý trung gian gỡ lỗi trả về kết quả</span> <span class="token keyword">class</span> <span class="token class-name">AnyService</span> <span class="token punctuation">{</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> isProcessing <span class="token operator">=</span> <span class="token boolean">false</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> queue <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token comment">// Chúng ta sẽ cần một method để chạy lại những xử lý lỗi</span> <span class="token comment">// được tạm dừng trước đó</span> <span class="token comment">// Nếu là "isError" === true thì các xử lý lỗi trong "queue"</span> <span class="token comment">// sẽ đều phải trả về lỗi (trường hợp xử lý trung gian gỡ lỗi chạy nhưng không thành công)</span> <span class="token comment">// Ngược lại sẽ trả về dữ liệu mong muốn và sẽ là đầu vào cho</span> <span class="token comment">// những xử lý đã tạm dừng trước đó</span> <span class="token function">executeQueue</span> <span class="token punctuation">(</span> <span class="token parameter">isError <span class="token punctuation">,</span> data</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> isError <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> queue <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter"><span class="token punctuation">[</span> _ <span class="token punctuation">,</span> reject <span class="token punctuation">]</span></span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">reject</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> isError <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> queue <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter"><span class="token punctuation">[</span> resolve <span class="token punctuation">]</span></span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">resolve</span> <span class="token punctuation">(</span> data <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// Đây sẽ là phần xử lý handle chính</span> <span class="token comment">// Đầu tiên chúng ta sẽ bắt lỗi theo mã lỗi mong muốn `DESIRED_ERROR_CODE`</span> <span class="token comment">// Tiếp đến sẽ có 2 trường hợp có thể xảy ra</span> <span class="token comment">// 1. Có một xử lý lỗi được handle trước đó (isProcessing === true)</span> <span class="token comment">// => Trường hợp này thì những xử lý lỗi tương tự sau đó</span> <span class="token comment">// chúng ta sẽ đưa vào "queue"</span> <span class="token comment">// và sẽ xử lý khi method "executeQueue" được gọi (khi gỡ lỗi kết thúc)</span> <span class="token comment">// 2. Chưa có xử lý lỗi nào được handle trước đó</span> <span class="token comment">// => Trường hợp này chúng ta sẽ gọi một xử lý trung gian gỡ lỗi</span> <span class="token comment">// để lấy thêm thông tin để khi gọi lại xử lý bị lỗi đầu tiên</span> <span class="token comment">// và những xử lý lỗi đã tạm dừng trước đó một lần nữa</span> <span class="token comment">// thì sẽ không bị lỗi</span> <span class="token comment">// => Đồng thời ở tại đây chúng ta sẽ gọi "executeQueue" tương ứng</span> <span class="token comment">// với kết quả trả về từ xử lý trung gian (thành công hoặc thất bại)</span> <span class="token function">handleError</span> <span class="token punctuation">(</span> <span class="token parameter">error <span class="token punctuation">,</span> config</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> error <span class="token punctuation">.</span> code <span class="token operator">===</span> <span class="token constant">DESIRED_ERROR_CODE</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> isProcessing <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">'currently processing => Need push to queue => Run after'</span> <span class="token punctuation">,</span> config <span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">resolve <span class="token punctuation">,</span> reject</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> queue <span class="token punctuation">.</span> <span class="token function">push</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> resolve <span class="token punctuation">,</span> reject <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">data</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">'execute: '</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token operator">...</span> config <span class="token punctuation">,</span> <span class="token operator">...</span> data <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">executeSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token operator">...</span> config <span class="token punctuation">,</span> <span class="token operator">...</span> data <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">catch</span> <span class="token punctuation">(</span> <span class="token parameter">error</span> <span class="token operator">=></span> Promise <span class="token punctuation">.</span> <span class="token function">reject</span> <span class="token punctuation">(</span> error <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> isProcessing <span class="token operator">=</span> <span class="token boolean">true</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">'processing...'</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> a <span class="token operator">:</span> <span class="token number">4</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">executeSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> a <span class="token operator">:</span> <span class="token number">4</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">data</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">'this.queue'</span> <span class="token punctuation">,</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> queue <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">executeSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token operator">...</span> config <span class="token punctuation">,</span> c <span class="token operator">:</span> <span class="token number">5</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">executeQueue</span> <span class="token punctuation">(</span> <span class="token boolean">false</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> c <span class="token operator">:</span> <span class="token number">5</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> isProcessing <span class="token operator">=</span> <span class="token boolean">false</span> Promise <span class="token punctuation">.</span> <span class="token function">resolve</span> <span class="token punctuation">(</span> data <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">catch</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">error</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">executeQueue</span> <span class="token punctuation">(</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> isProcessing <span class="token operator">=</span> <span class="token boolean">false</span> Promise <span class="token punctuation">.</span> <span class="token function">reject</span> <span class="token punctuation">(</span> error <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> Promise <span class="token punctuation">.</span> <span class="token function">reject</span> <span class="token punctuation">(</span> error <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// Đây sẽ là method dùng để gọi khi đã khởi tạo class</span> <span class="token keyword">async</span> <span class="token function">executeSomething</span> <span class="token punctuation">(</span> <span class="token parameter">config <span class="token punctuation">,</span> needReject</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">doSomething</span> <span class="token punctuation">(</span> config <span class="token punctuation">,</span> needReject <span class="token punctuation">)</span> <span class="token keyword">return</span> Promise <span class="token punctuation">.</span> <span class="token function">resolve</span> <span class="token punctuation">(</span> config <span class="token punctuation">,</span> data <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> error <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">handleError</span> <span class="token punctuation">(</span> error <span class="token punctuation">,</span> config <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Tại đây chúng ta sẽ khởi tạo một thể hiển của "AnyService"</span> <span class="token comment">// và call 3 xử lý lỗi ngay sau đó</span> <span class="token keyword">const</span> service <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AnyService</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> service <span class="token punctuation">.</span> <span class="token function">executeSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> a <span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> service <span class="token punctuation">.</span> <span class="token function">executeSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> a <span class="token operator">:</span> <span class="token number">2</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> service <span class="token punctuation">.</span> <span class="token function">executeSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> a <span class="token operator">:</span> <span class="token number">3</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token comment">// Kết quả nhận được sẽ như dưới</span> <span class="token comment">// run "doSomething async" with: {a: 1}</span> <span class="token comment">// processing... {a: 4}</span> <span class="token comment">// run "doSomething async" with: {a: 2}</span> <span class="token comment">// currently processing => Need push to queue => Run after {a: 2}</span> <span class="token comment">// run "doSomething async" with: {a: 3}</span> <span class="token comment">// currently processing => Need push to queue => Run after {a: 3}</span> <span class="token comment">// run "doSomething async" with: {a: 4}</span> <span class="token comment">// this.queue (2) [Array(2), Array(2)]</span> <span class="token comment">// execute: {a: 2, c: 5}</span> <span class="token comment">// execute: {a: 3, c: 5}</span> <span class="token comment">// run "doSomething async" with: {a: 1, c: 5}</span> <span class="token comment">// run "doSomething async" with: {a: 2, c: 5}</span> <span class="token comment">// run "doSomething async" with: {a: 3, c: 5}</span> |
With the above code, we can temporarily handle according to the original requirements. Problems will arise around but will be insignificant.
In the above code, we will need to note a core point for the smoothest management of Promise
. It is the handle that pauses the error handling when there was a previous error handling and calls back the paused error handling. Here we will use the method that except for the first error handling, the following error handling will be generated corresponding to a Promise
instance. These Promise
instances will do the job of storing the two callback resolve
and reject
handlers in the queue
and will always be pending
during the middle of the processing call when the result comes in (since we don’t calls resolve
or reject
right after that, which will only run when the executeQueue
method is called).
It’s interesting, isn’t it, somehow we have halted processing and can even manipulate those threads right after the debug intermediate processing has got results.
3. Conclusion
So I have completed the combination of class
and Promise
used to manage and manipulate asynchronous processing or Promise
. This combination relies heavily on the Promise
concept as well as the Promise
state, partly that our variables are synchronized in a class
instance. Maybe next time try to combine Closures
and Promise
see stars.
My article is over here. Hopefully it will bring benefits to you and have interesting solutions to Promise
. See you in the next article. Hello!