1# Functions that contain the `await` keyword will compile into async functions,
2# supported by Node 7.6+, Chrome 55+, Firefox 52+, Safari 10.1+ and Edge.
3# But runtimes that don’t support the `await` keyword will throw an error just
4# from parsing this file, even without executing it, even if we put
5# `return unless try new Function 'async () => {}'` at the top of this file.
6# Therefore we need to prevent runtimes which will choke on such code from
7# parsing it, which is handled in `Cakefile`.
8
9
10# This is always fulfilled.
11winning = (val) -> Promise.resolve val
12
13# This is always rejected.
14failing = (val) -> Promise.reject new Error val
15
16
17test "async as argument", ->
18  ok ->
19    await winning()
20
21test "explicit async", ->
22  a = do ->
23    await return 5
24  eq a.constructor, Promise
25  a.then (val) ->
26    eq val, 5
27
28test "implicit async", ->
29  a = do ->
30    x = await winning(5)
31    y = await winning(4)
32    z = await winning(3)
33    [x, y, z]
34
35  eq a.constructor, Promise
36
37test "async return value (implicit)", ->
38  out = null
39  a = ->
40    x = await winning(5)
41    y = await winning(4)
42    z = await winning(3)
43    [x, y, z]
44
45  b = do ->
46    out = await a()
47
48  b.then ->
49    arrayEq out, [5, 4, 3]
50
51test "async return value (explicit)", ->
52  out = null
53  a = ->
54    await return [5, 2, 3]
55
56  b = do ->
57    out = await a()
58
59  b.then ->
60    arrayEq out, [5, 2, 3]
61
62
63test "async parameters", ->
64  [out1, out2] = [null, null]
65  a = (a, [b, c])->
66    arr = [a]
67    arr.push b
68    arr.push c
69    await return arr
70
71  b = (a, b, c = 5)->
72    arr = [a]
73    arr.push b
74    arr.push c
75    await return arr
76
77  c = do ->
78    out1 = await a(5, [4, 3])
79    out2 = await b(4, 4)
80
81  c.then ->
82    arrayEq out1, [5, 4, 3]
83    arrayEq out2, [4, 4, 5]
84
85test "async `this` scoping", ->
86  bnd = null
87  ubnd = null
88  nst = null
89  obj =
90    bound: ->
91      return do =>
92        await return this
93    unbound: ->
94      return do ->
95        await return this
96    nested: ->
97      return do =>
98        await do =>
99          await do =>
100            await return this
101
102  promise = do ->
103    bnd = await obj.bound()
104    ubnd = await obj.unbound()
105    nst = await obj.nested()
106
107  promise.then ->
108    eq bnd, obj
109    ok ubnd isnt obj
110    eq nst, obj
111
112test "await precedence", ->
113  out = null
114
115  fn = (win, fail) ->
116    win(3)
117
118  promise = do ->
119    # assert precedence between unary (new) and power (**) operators
120    out = 1 + await new Promise(fn) ** 2
121
122  promise.then ->
123    eq out, 10
124
125test "`await` inside IIFEs", ->
126  [x, y, z] = new Array(3)
127
128  a = do ->
129    x = switch (4)  # switch 4
130      when 2
131        await winning(1)
132      when 4
133        await winning(5)
134      when 7
135        await winning(2)
136
137    y = try
138      text = "this should be caught"
139      throw new Error(text)
140      await winning(1)
141    catch e
142      await winning(4)
143
144    z = for i in [0..5]
145      a = i * i
146      await winning(a)
147
148  a.then ->
149    eq x, 5
150    eq y, 4
151
152    arrayEq z, [0, 1, 4, 9, 16, 25]
153
154test "error handling", ->
155  res = null
156  val = 0
157  a = ->
158    try
159      await failing("fail")
160    catch e
161      val = 7  # to assure the catch block runs
162      return e
163
164  b = do ->
165    res = await a()
166
167  b.then ->
168    eq val, 7
169
170    ok res.message?
171    eq res.message, "fail"
172
173test "await expression evaluates to argument if not A+", ->
174  eq(await 4, 4)
175
176
177test "implicit call with `await`", ->
178  addOne = (arg) -> arg + 1
179
180  a = addOne await 3
181  eq a, 4
182
183test "async methods in classes", ->
184  class Base
185    @static: ->
186      await 1
187    method: ->
188      await 2
189
190  eq await Base.static(), 1
191  eq await new Base().method(), 2
192
193  class Child extends Base
194    @static: -> super()
195    method: -> super()
196
197  eq await Child.static(), 1
198  eq await new Child().method(), 2
199
200test "#3199: await multiline implicit object", ->
201  do ->
202    y =
203      if no then await
204        type: 'a'
205        msg: 'b'
206    eq undefined, y
207
208test "top-level await", ->
209  eqJS 'await null', 'await null;'
210
211test "top-level wrapper has correct async attribute", ->
212  starts = (code, prefix) ->
213    compiled = CoffeeScript.compile code
214    unless compiled.startsWith prefix
215      fail """Expected generated JavaScript to start with:
216        #{reset}#{prefix}#{red}
217        but instead it was:
218        #{reset}#{compiled}#{red}"""
219  starts 'await null', '(async function'
220  starts 'do -> await null', '(function'
221