promises.scm 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. ;;; Promises
  2. ;;; Copyright (C) 2024 David Thompson <dave@spritely.institute>
  3. ;;;
  4. ;;; Licensed under the Apache License, Version 2.0 (the "License");
  5. ;;; you may not use this file except in compliance with the License.
  6. ;;; You may obtain a copy of the License at
  7. ;;;
  8. ;;; http://www.apache.org/licenses/LICENSE-2.0
  9. ;;;
  10. ;;; Unless required by applicable law or agreed to in writing, software
  11. ;;; distributed under the License is distributed on an "AS IS" BASIS,
  12. ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. ;;; See the License for the specific language governing permissions and
  14. ;;; limitations under the License.
  15. ;;; Commentary:
  16. ;;;
  17. ;;; Simple JavaScript-style promises for testing fibers.
  18. ;;;
  19. ;;; Code:
  20. (define-module (hoot promises)
  21. #:use-module (ice-9 match)
  22. #:use-module (ice-9 q)
  23. #:use-module (srfi srfi-9)
  24. #:use-module (srfi srfi-9 gnu)
  25. #:export (make-promise
  26. promise?
  27. on)
  28. #:replace (make-promise
  29. promise?))
  30. (define-record-type <promise>
  31. (%make-promise state values resolve-queue reject-queue)
  32. promise?
  33. (state promise-state set-promise-state!)
  34. (values promise-values set-promise-values!)
  35. (resolve-queue promise-resolve-queue)
  36. (reject-queue promise-reject-queue))
  37. (define (print-promise promise port)
  38. (format port "#<promise ~a>" (object-address promise)))
  39. (set-record-type-printer! <promise> print-promise)
  40. (define (make-promise proc)
  41. (let* ((resolve-q (make-q))
  42. (reject-q (make-q))
  43. (promise (%make-promise 'pending #f resolve-q reject-q)))
  44. (define (flush-q q vals)
  45. (let lp ()
  46. (unless (q-empty? q)
  47. (apply (deq! q) vals)
  48. (lp))))
  49. (define (resolve . vals)
  50. (set-promise-state! promise 'resolved)
  51. (set-promise-values! promise vals)
  52. (flush-q resolve-q vals))
  53. (define (reject . vals)
  54. (set-promise-state! promise 'rejected)
  55. (set-promise-values! promise vals)
  56. (flush-q reject-q vals))
  57. (proc resolve reject)
  58. promise))
  59. ;; Using Goblins style 'on' rather than 'then' because it reads nicer
  60. ;; as a prefix.
  61. (define* (on promise on-resolve #:optional (on-reject values))
  62. (make-promise
  63. (lambda (resolve reject)
  64. (define (resolve* . vals)
  65. (call-with-values (lambda () (apply on-resolve vals))
  66. (lambda vals
  67. (apply resolve vals))))
  68. (define (reject* . vals)
  69. (call-with-values (lambda () (apply on-reject vals))
  70. (lambda vals
  71. (apply reject vals))))
  72. (match promise
  73. (($ <promise> 'pending _ resolve-q reject-q)
  74. (enq! resolve-q resolve*)
  75. (enq! reject-q reject*))
  76. (($ <promise> 'resolved vals)
  77. (apply resolve* vals))
  78. (($ <promise> 'rejected vals)
  79. (apply reject* vals))))))