Skip to content

Commit ffd4400

Browse files
authored
Merge pull request #752 from vim-jp/promise-chain
Async.Promise: Add "chain" function
2 parents 80887bd + 8e4f446 commit ffd4400

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed

autoload/vital/__vital__/Async/Promise.vim

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,27 @@ function! s:wait(promise, ...) abort
270270
endif
271271
endfunction
272272

273+
function! s:chain(promise_factories) abort
274+
return s:_chain(copy(a:promise_factories), [])
275+
endfunction
276+
277+
function! s:_chain(promise_factories, results) abort
278+
if len(a:promise_factories) is# 0
279+
return s:resolve(a:results)
280+
endif
281+
let Factory = remove(a:promise_factories, 0)
282+
try
283+
return Factory()
284+
\.then({ v -> add(a:results, v) })
285+
\.then({ -> s:_chain(a:promise_factories, a:results) })
286+
catch
287+
return s:reject({
288+
\ 'exception': v:exception,
289+
\ 'throwpoint': v:throwpoint,
290+
\})
291+
endtry
292+
endfunction
293+
273294
let s:_on_unhandled_rejection = s:NOOP
274295
function! s:on_unhandled_rejection(on_unhandled_rejection) abort
275296
let s:_on_unhandled_rejection = a:on_unhandled_rejection

doc/vital/Async/Promise.txt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,42 @@ wait({promise}[, {options}]) *Vital.Async.Promise.wait()*
377377
call Promise.wait(p, 1000)
378378
" Is equivalent to call Promise.wait(p, {'timeout': 1000})
379379
>
380+
chain({promise_factories}) *Vital.Async.Promise.chain()*
381+
Chain promises produced by {promise_factories} (|List| of |Function|)
382+
and return a promise which resolves to a result |List| which contains
383+
result of each produced promises.
384+
It is like an asynchronous sequential call. It rejects when one of
385+
function in {promise_factories} has failed or produced promises
386+
rejects. Note that it stops producing promises by functions after
387+
rejection.
388+
>
389+
let fs = [
390+
\ { -> Promise.new({ r -> timer_start(50, { -> r('1') })})},
391+
\ { -> Promise.new({ r -> timer_start(50, { -> r('2') })})},
392+
\ { -> Promise.new({ r -> timer_start(50, { -> r('3') })})},
393+
\]
394+
call Promise.chain(fs)
395+
" --------1--------2--------3----> RESOLVE
396+
" 50ms 100ms 150ms
397+
398+
let fs = [
399+
\ { -> Promise.new({ r -> timer_start(50, { -> r('1') })})},
400+
\ { -> execute('throw "Error"') },
401+
\ { -> Promise.new({ r -> timer_start(50, { -> r('3') })})},
402+
\]
403+
call Promise.chain(fs)
404+
" --------1----> REJECT
405+
" 50ms
406+
407+
let fs = [
408+
\ { -> Promise.new({ r -> timer_start(50, { -> r('1') })})},
409+
\ { -> Promise.new({ _, rj -> timer_start(50, { -> rj('2') })})},
410+
\ { -> Promise.new({ r -> timer_start(50, { -> r('3') })})},
411+
\]
412+
call Promise.chain(fs)
413+
" --------1--------2----> REJECT
414+
" 50ms 100ms
415+
<
380416
on_unhandled_rejection({callback}) *Vital.Async.Promise.on_unhandled_rejection*
381417

382418
Set callback to catch all unhandled rejected promise's result.

test/Async/Promise.vimspec

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,78 @@ Describe Async.Promise
805805
End
806806
End
807807

808+
Describe .chain()
809+
It returns a promise which resolves to an empty List when an empty factories has specified
810+
let l = l:
811+
let p = P.chain([]).then({ v -> extend(l, { 'result': v })})
812+
call s:wait_has_key(l, 'result')
813+
Assert Equals(result, [])
814+
Assert Equals(p._state, FULFILLED)
815+
End
816+
817+
It sequentially calls functions in factories and resolves promises
818+
let l = l:
819+
let fs = [
820+
\ { -> P.resolve('Hello') },
821+
\ { -> P.resolve('World') },
822+
\ { -> P.resolve('Goodbye') },
823+
\]
824+
let p = P.chain(fs).then({ v -> extend(l, { 'result': v })})
825+
call s:wait_has_key(l, 'result')
826+
Assert Equals(result, ['Hello', 'World', 'Goodbye'])
827+
Assert Equals(p._state, FULFILLED)
828+
End
829+
830+
It sequentially calls functions in factories but reject promises if intermediate function call fails
831+
let l = l:
832+
call P.on_unhandled_rejection({ result -> extend(l, { 'result': result }) })
833+
834+
let fs = [
835+
\ { -> P.resolve('Hello').then({ -> extend(l, { 'Hello': 1 }) }) },
836+
\ { -> execute('throw HOGE') },
837+
\ { -> P.resolve('Goodbye').then({ -> extend(l, { 'Goodbye': 1 }) }) },
838+
\]
839+
let p = P.chain(fs).then({ -> execute('throw "error"') })
840+
call s:wait_has_key(l, 'result')
841+
Assert HasKey(result, 'exception')
842+
Assert HasKey(result, 'throwpoint')
843+
Assert Equals(p._state, REJECTED)
844+
Assert HasKey(l, 'Hello')
845+
Assert KeyNotExists(l, 'Goodbye')
846+
End
847+
848+
It sequentially calls functions in factories but reject promises if intermediate promise reject
849+
let l = l:
850+
call P.on_unhandled_rejection({ result -> extend(l, { 'result': result }) })
851+
852+
let fs = [
853+
\ { -> P.resolve('Hello').then({ -> extend(l, { 'Hello': 1 }) }) },
854+
\ { -> P.resolve('World').then({ -> extend(l, { 'World': 1 }) }).then({ -> P.reject('Wow')}) },
855+
\ { -> P.resolve('Goodbye').then({ -> extend(l, { 'Goodbye': 1 }) }) },
856+
\]
857+
let p = P.chain(fs).then({ -> execute('throw "error"') })
858+
call s:wait_has_key(l, 'result')
859+
Assert Equals(result, 'Wow')
860+
Assert Equals(p._state, REJECTED)
861+
Assert HasKey(l, 'Hello')
862+
Assert HasKey(l, 'World')
863+
Assert KeyNotExists(l, 'Goodbye')
864+
End
865+
866+
It sequentially calls functions in factories beyonds &maxfuncdepth and resolves promises
867+
let l = l:
868+
let fs = map(
869+
\ range(&maxfuncdepth * 2),
870+
\ { k -> { -> P.new({r -> r(k) }) } },
871+
\)
872+
let p = P.chain(fs).then({ v -> extend(l, { 'result': v })})
873+
call s:wait_has_key(l, 'result')
874+
Assert Equals(len(result), &maxfuncdepth * 2)
875+
Assert Equals(result, range(&maxfuncdepth * 2))
876+
Assert Equals(p._state, FULFILLED)
877+
End
878+
End
879+
808880
Describe .on_unhandled_rejection
809881

810882
After each

0 commit comments

Comments
 (0)