Tương lai JavaScript so với lời hứa

Qua. Philipp Haller, Aleksandar Prokopec, Heather Miller, Viktor Klang, Roland Kuhn và Vojin Jovanovic

Giới thiệu

Hợp đồng tương lai cung cấp một cách lý luận về việc thực hiện nhiều hoạt động song song– một cách hiệu quả và không bị chặn. Một

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 là một đối tượng giữ chỗ cho một giá trị có thể chưa tồn tại. Nói chung, giá trị của Tương lai được cung cấp đồng thời và sau đó có thể được sử dụng. Soạn các tác vụ đồng thời theo cách này có xu hướng dẫn đến mã song song nhanh hơn, không đồng bộ, không chặn

Theo mặc định, các hợp đồng tương lai và lời hứa không bị chặn, sử dụng các cuộc gọi lại thay vì các hoạt động chặn thông thường. Để đơn giản hóa việc sử dụng các lệnh gọi lại cả về mặt cú pháp và khái niệm, Scala cung cấp các bộ kết hợp như

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
3,
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
4 và
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
5 được sử dụng để soạn hợp đồng tương lai theo cách không chặn. Vẫn có thể chặn - đối với những trường hợp thực sự cần thiết, hợp đồng tương lai có thể bị chặn (mặc dù điều này không được khuyến khích)

Một tương lai điển hình trông như thế này

val inverseFuture: Future[Matrix] = Future {
  fatMatrix.inverse() // non-blocking long lasting computation
}(executionContext)

Hoặc với thành ngữ hơn

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed

Cả hai đoạn mã đều ủy thác việc thực thi

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
6 cho một
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7 và thể hiện kết quả tính toán trong
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
8

Bối cảnh thực hiện

Tương lai và Lời hứa xoay quanh

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7s, chịu trách nhiệm thực hiện các tính toán

Một

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7 tương tự như một Executor. có thể tự do thực hiện các tính toán trong một luồng mới, trong một luồng gộp hoặc trong luồng hiện tại (mặc dù việc thực hiện tính toán trong luồng hiện tại không được khuyến khích – hãy nói thêm về điều đó bên dưới)

Gói

implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
1 được đưa ra khỏi hộp với triển khai
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7, một nhóm luồng tĩnh toàn cầu. Cũng có thể chuyển đổi một
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
3 thành một
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7. Cuối cùng, người dùng có thể tự do mở rộng đặc điểm
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7 để triển khai bối cảnh thực thi của riêng họ, mặc dù điều này chỉ nên được thực hiện trong một số trường hợp hiếm hoi

Bối cảnh thực thi toàn cầu

implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
6 là một
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7 được hỗ trợ bởi ForkJoinPool. Nó là đủ cho hầu hết các tình huống nhưng cần phải cẩn thận. Một
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
8 quản lý một số luồng giới hạn (số lượng luồng tối đa được gọi là mức xử lý song song). Số lượng tính toán chặn đồng thời chỉ có thể vượt quá mức xử lý song song nếu mỗi lệnh gọi chặn được bao bọc bên trong một lệnh gọi
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
9 (thêm về điều đó bên dưới). Mặt khác, có nguy cơ nhóm luồng trong bối cảnh thực thi toàn cầu bị bỏ đói và không thể tiến hành tính toán

Theo mặc định,

implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
6 đặt mức độ song song của nhóm liên kết rẽ nhánh cơ bản của nó thành số lượng bộ xử lý có sẵn (). Cấu hình này có thể được ghi đè bằng cách đặt một (hoặc nhiều) thuộc tính VM sau

  • scala. đồng thời. định nghĩa bài văn. minThreads - mặc định là
    given ExecutionContext = ExecutionContext.global
    
    for i <- 1 to 32000 do
      Future {
        blocking {
          Thread.sleep(999999)
        }
      }
    
    1
  • scala. đồng thời. định nghĩa bài văn. numThreads - có thể là một số hoặc một số nhân (N) ở dạng 'xN' ;
  • scala. đồng thời. định nghĩa bài văn. maxThreads - mặc định là
    given ExecutionContext = ExecutionContext.global
    
    for i <- 1 to 32000 do
      Future {
        blocking {
          Thread.sleep(999999)
        }
      }
    
    1

Mức độ song song sẽ được đặt thành

given ExecutionContext = ExecutionContext.global

for i <- 1 to 32000 do
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
4 miễn là nó vẫn nằm trong khoảng
given ExecutionContext = ExecutionContext.global

for i <- 1 to 32000 do
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
5

Như đã nêu ở trên,

implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
8 có thể tăng số lượng luồng vượt quá
given ExecutionContext = ExecutionContext.global

for i <- 1 to 32000 do
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
7 của nó khi có tính toán chặn. Như đã giải thích trong API
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
8, điều này chỉ khả thi nếu nhóm được thông báo rõ ràng

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}

May mắn thay, gói đồng thời cung cấp một cách thuận tiện để làm như vậy

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}

Lưu ý rằng

implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
9 là một cấu trúc chung sẽ được thảo luận sâu hơn

Cuối cùng nhưng không kém phần quan trọng, bạn phải nhớ rằng

implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
8 không được thiết kế cho các hoạt động ngăn chặn lâu dài. Ngay cả khi được thông báo bằng
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
9, nhóm có thể không sinh ra công nhân mới như bạn mong đợi và khi công nhân mới được tạo, chúng có thể lên tới 32767. Để cung cấp cho bạn một ý tưởng, đoạn mã sau sẽ sử dụng 32000 chủ đề

implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}

given ExecutionContext = ExecutionContext.global

for i <- 1 to 32000 do
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }

Nếu bạn cần bao bọc các hoạt động chặn lâu dài, chúng tôi khuyên bạn nên sử dụng một

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7 chuyên dụng, chẳng hạn bằng cách bao bọc một Java
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
3

Điều chỉnh Trình thực thi Java

Sử dụng phương pháp

ExecutionContext.fromExecutor(new ThreadPoolExecutor( /* your configuration */ ))
4, bạn có thể bọc một Java
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
3 thành một
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7. Ví dụ

ExecutionContext.fromExecutor(new ThreadPoolExecutor( /* your configuration */ ))

ExecutionContext.fromExecutor(ThreadPoolExecutor( /* your configuration */ ))

Bối cảnh thực thi đồng bộ

Người ta có thể muốn có một

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7 chạy các phép tính trong luồng hiện tại

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
0

Điều này nên tránh vì nó đưa ra thuyết không tất định trong việc thực hiện tương lai của bạn

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
1

Lệnh gọi

ExecutionContext.fromExecutor(new ThreadPoolExecutor( /* your configuration */ ))
8 có thể thực thi trong luồng của
ExecutionContext.fromExecutor(new ThreadPoolExecutor( /* your configuration */ ))
9 hoặc trong luồng chính và do đó có thể là không đồng bộ hoặc đồng bộ. Như đã giải thích ở đây, một cuộc gọi lại không nên là cả hai

Một

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 là một đối tượng chứa một giá trị có thể trở nên khả dụng tại một thời điểm nào đó. Giá trị này thường là kết quả của một số tính toán khác

  1. Nếu phép tính chưa hoàn thành, chúng tôi nói rằng ____________2 chưa hoàn thành
  2. Nếu tính toán đã hoàn thành với một giá trị hoặc với một ngoại lệ, chúng tôi nói rằng ____0_______2 đã hoàn thành

Hoàn thành có thể có một trong hai hình thức

  1. Khi một
    import scala.concurrent.Future
    import scala.concurrent.blocking
    
    Future {
      blocking {
        myLock.lock()
        // ...
      }
    }
    
    2 được hoàn thành với một giá trị, chúng tôi nói rằng tương lai đã được hoàn thành thành công với giá trị đó
  2. Khi một
    import scala.concurrent.Future
    import scala.concurrent.blocking
    
    Future {
      blocking {
        myLock.lock()
        // ...
      }
    }
    
    2 được hoàn thành với một ngoại lệ do máy tính đưa ra, chúng ta nói rằng
    import scala.concurrent.Future
    import scala.concurrent.blocking
    
    Future {
      blocking {
        myLock.lock()
        // ...
      }
    }
    
    2 đã thất bại với ngoại lệ đó

Một

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 có một thuộc tính quan trọng mà nó chỉ có thể được chỉ định một lần. Khi một đối tượng
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 được cung cấp một giá trị hoặc một ngoại lệ, nó sẽ có hiệu lực bất biến – nó không bao giờ có thể bị ghi đè

Cách đơn giản nhất để tạo một đối tượng tương lai là gọi phương thức

ExecutionContext.fromExecutor(ThreadPoolExecutor( /* your configuration */ ))
8 bắt đầu tính toán không đồng bộ và trả về một tương lai chứa kết quả của phép tính đó. Kết quả sẽ có sẵn khi tương lai hoàn thành

Lưu ý rằng

ExecutionContext.fromExecutor(ThreadPoolExecutor( /* your configuration */ ))
9 là một loại biểu thị các đối tượng trong tương lai, trong khi đó
ExecutionContext.fromExecutor(ThreadPoolExecutor( /* your configuration */ ))
8 là một phương thức tạo và lên lịch cho một phép tính không đồng bộ, sau đó trả về một đối tượng trong tương lai sẽ được hoàn thành với kết quả của phép tính đó

Điều này được thể hiện rõ nhất thông qua một ví dụ

Giả sử rằng chúng ta muốn sử dụng API giả định của một số mạng xã hội phổ biến để lấy danh sách bạn bè cho một người dùng nhất định. Chúng tôi sẽ mở một phiên mới và sau đó gửi yêu cầu lấy danh sách bạn bè của một người dùng cụ thể

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
2

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
3

Ở trên, trước tiên chúng tôi nhập nội dung của gói

implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
1 để hiển thị loại
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2. Chúng tôi sẽ sớm giải thích lần nhập thứ hai

Sau đó, chúng tôi khởi tạo một biến phiên mà chúng tôi sẽ sử dụng để gửi yêu cầu đến máy chủ, sử dụng phương pháp

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
03 giả định. Để có được danh sách bạn bè của người dùng, yêu cầu phải được gửi qua mạng, quá trình này có thể mất nhiều thời gian. Điều này được minh họa bằng lệnh gọi phương thức
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
04 trả về
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
05. Để sử dụng CPU tốt hơn cho đến khi có phản hồi, chúng ta không nên chặn phần còn lại của chương trình – quá trình tính toán này phải được lên lịch không đồng bộ. Phương thức
ExecutionContext.fromExecutor(ThreadPoolExecutor( /* your configuration */ ))
8 thực hiện chính xác điều đó– nó thực hiện đồng thời khối tính toán đã chỉ định, trong trường hợp này là gửi yêu cầu đến máy chủ và chờ phản hồi

Danh sách bạn bè sẽ có trong tương lai

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
07 sau khi máy chủ phản hồi

Một nỗ lực không thành công có thể dẫn đến một ngoại lệ. Trong ví dụ sau, giá trị

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
08 được khởi tạo không chính xác, vì vậy phép tính trong khối
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 sẽ tạo ra một giá trị
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
10.
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
07 trong tương lai này sau đó không thành công với ngoại lệ này thay vì được hoàn thành thành công

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
4

Dòng

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
12 ở trên nhập ngữ cảnh thực thi chung mặc định. Bối cảnh thực thi thực thi các tác vụ được gửi cho chúng và bạn có thể coi bối cảnh thực thi là nhóm luồng. Chúng rất cần thiết cho phương pháp
ExecutionContext.fromExecutor(ThreadPoolExecutor( /* your configuration */ ))
8 vì chúng xử lý cách thức và thời điểm thực hiện tính toán không đồng bộ. Bạn có thể xác định bối cảnh thực thi của riêng mình và sử dụng chúng với
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2, nhưng hiện tại bạn chỉ cần biết rằng bạn có thể nhập bối cảnh thực thi mặc định như minh họa ở trên là đủ

Ví dụ của chúng tôi dựa trên API mạng xã hội giả định trong đó quá trình tính toán bao gồm gửi yêu cầu mạng và chờ phản hồi. Thật công bằng khi đưa ra một ví dụ liên quan đến tính toán không đồng bộ mà bạn có thể thử ngay lập tức. Giả sử bạn có một tệp văn bản và bạn muốn tìm vị trí xuất hiện đầu tiên của một từ khóa cụ thể. Tính toán này có thể liên quan đến việc chặn trong khi nội dung tệp đang được truy xuất từ ​​​​đĩa, vì vậy sẽ hợp lý khi thực hiện nó đồng thời với phần còn lại của tính toán

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
5

gọi lại

Bây giờ chúng tôi biết cách bắt đầu tính toán không đồng bộ để tạo ra một giá trị mới trong tương lai, nhưng chúng tôi chưa chỉ ra cách sử dụng kết quả khi nó có sẵn, để chúng tôi có thể làm điều gì đó hữu ích với nó. Chúng ta thường quan tâm đến kết quả tính toán, không chỉ các tác dụng phụ của nó

Trong nhiều triển khai trong tương lai, một khi khách hàng của tương lai quan tâm đến kết quả của nó, nó phải chặn tính toán của chính nó và đợi cho đến khi tương lai hoàn thành– chỉ khi đó, nó mới có thể sử dụng giá trị của tương lai để tiếp tục tính toán của chính mình. Mặc dù điều này được cho phép bởi API Scala

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 như chúng tôi sẽ trình bày sau, nhưng từ quan điểm hiệu suất, cách tốt hơn để làm điều đó là theo cách hoàn toàn không bị chặn, bằng cách đăng ký gọi lại trong tương lai. Cuộc gọi lại này được gọi không đồng bộ sau khi hoàn thành tương lai. Nếu tương lai đã được hoàn thành khi đăng ký cuộc gọi lại, thì cuộc gọi lại có thể được thực hiện không đồng bộ hoặc tuần tự trên cùng một luồng

Hình thức đăng ký gọi lại chung nhất là sử dụng phương thức

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
16, phương thức này nhận hàm gọi lại kiểu
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
17. Cuộc gọi lại được áp dụng cho giá trị của loại
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
18 nếu tương lai hoàn thành thành công hoặc cho giá trị của loại
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
19 nếu không

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
20 tương tự như
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
21 hoặc
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
22, ở chỗ nó là một đơn nguyên có khả năng nắm giữ một loại giá trị nào đó. Tuy nhiên, nó đã được thiết kế đặc biệt để giữ một giá trị hoặc một số đối tượng có thể ném được. Trường hợp một
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
21 có thể là một giá trị (i. e.
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
24) hoặc không có giá trị gì cả (i. e.
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
25),
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
20 là một
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
18 khi nó giữ một giá trị và nếu không thì
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
19, giữ một ngoại lệ.
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
19 nắm giữ nhiều thông tin hơn là chỉ một
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
25 đơn giản bằng cách cho biết tại sao giá trị không có ở đó. Đồng thời, bạn có thể coi
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
20 là phiên bản đặc biệt của
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
32, chuyên dùng cho trường hợp giá trị bên trái là
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33

Quay lại ví dụ về mạng xã hội của chúng tôi, giả sử chúng tôi muốn tìm nạp danh sách các bài đăng gần đây của chính mình và hiển thị chúng trên màn hình. Chúng tôi làm như vậy bằng cách gọi một phương thức

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
34 trả về một
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
35– một danh sách các bài viết văn bản gần đây

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
6

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
7

Phương pháp

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
16 nói chung theo nghĩa là nó cho phép khách hàng xử lý kết quả của cả các phép tính thất bại và thành công trong tương lai. Trong trường hợp chỉ cần xử lý kết quả thành công, có thể sử dụng lệnh gọi lại
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
4

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
8

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
9

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2s cung cấp một cách rõ ràng để chỉ xử lý các kết quả không thành công bằng cách sử dụng phép chiếu
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
39 để chuyển đổi một
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
40 thành một
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
41. Một ví dụ về cách làm này được cung cấp trong phần bên dưới về

Quay lại ví dụ trước với việc tìm kiếm lần xuất hiện đầu tiên của từ khóa, bạn có thể muốn in vị trí của từ khóa ra màn hình

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
0

Cả hai phương thức

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
16 và
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
4 đều có loại kết quả là
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
44, có nghĩa là các lệnh gọi của các phương thức này không thể được xâu chuỗi. Lưu ý rằng thiết kế này là có chủ ý, để tránh gợi ý rằng các lệnh gọi theo chuỗi có thể ngụ ý một thứ tự khi thực hiện các lệnh gọi lại đã đăng ký (các lệnh gọi lại đã đăng ký trong cùng một tương lai là không có thứ tự)

Điều đó nói rằng, bây giờ chúng ta nên bình luận về thời điểm gọi lại chính xác. Vì nó yêu cầu giá trị trong tương lai phải có sẵn, nên nó chỉ có thể được gọi sau khi tương lai hoàn thành. Tuy nhiên, không có gì đảm bảo rằng nó sẽ được gọi bởi chuỗi đã hoàn thành tương lai hoặc chuỗi đã tạo cuộc gọi lại. Thay vào đó, cuộc gọi lại được thực hiện bởi một số luồng, tại một số thời điểm sau khi hoàn thành đối tượng tương lai. Chúng tôi nói rằng cuộc gọi lại được thực hiện cuối cùng

Hơn nữa, thứ tự thực hiện các cuộc gọi lại không được xác định trước, ngay cả giữa các lần chạy khác nhau của cùng một ứng dụng. Trên thực tế, các callback có thể không được gọi tuần tự lần lượt mà có thể thực thi đồng thời cùng một lúc. Điều này có nghĩa là trong ví dụ sau, biến

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
45 có thể không được đặt thành đúng số lượng chữ thường và chữ hoa
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
46 ký tự từ văn bản được tính toán

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
1

Ở trên, hai cuộc gọi lại có thể thực hiện lần lượt từng cuộc gọi, trong trường hợp đó, biến

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
45 giữ giá trị mong đợi
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
48. Tuy nhiên, chúng cũng có thể thực hiện đồng thời, do đó,
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
45 có thể kết thúc bằng hoặc là
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
50 hoặc
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
51, vì
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
52 không phải là phép toán nguyên tử (i. e. nó bao gồm một bước đọc và ghi có thể xen kẽ tùy ý với các lần đọc và ghi khác)

Để hoàn thiện, ngữ nghĩa của các cuộc gọi lại được liệt kê ở đây

  1. Đăng ký một cuộc gọi lại

    implicit val ec: ExecutionContext = ...
    val inverseFuture : Future[Matrix] = Future {
      fatMatrix.inverse()
    } // ec is implicitly passed
    
    16 trong tương lai đảm bảo rằng lần đóng tương ứng được gọi sau khi tương lai được hoàn thành, cuối cùng

  2. Đăng ký một cuộc gọi lại

    import scala.concurrent.Future
    import scala.concurrent.blocking
    
    Future {
      blocking {
        myLock.lock()
        // ...
      }
    }
    
    4 có cùng ngữ nghĩa như
    implicit val ec: ExecutionContext = ...
    val inverseFuture : Future[Matrix] = Future {
      fatMatrix.inverse()
    } // ec is implicitly passed
    
    16, với sự khác biệt là việc đóng cửa chỉ được gọi nếu tương lai được hoàn thành thành công

  3. Đăng ký một cuộc gọi lại trong tương lai đã được hoàn thành sẽ dẫn đến cuộc gọi lại được thực hiện cuối cùng (như được ngụ ý bởi 1)

  4. Trong trường hợp nhiều cuộc gọi lại được đăng ký trong tương lai, thứ tự thực hiện chúng không được xác định. Trên thực tế, các cuộc gọi lại có thể được thực hiện đồng thời với nhau. Tuy nhiên, một triển khai

    import scala.concurrent.Future
    import scala.concurrent.blocking
    
    Future {
      blocking {
        myLock.lock()
        // ...
      }
    }
    
    7 cụ thể có thể dẫn đến một thứ tự được xác định rõ

  5. Trong trường hợp một số cuộc gọi lại đưa ra một ngoại lệ, các cuộc gọi lại khác sẽ được thực hiện bất kể

  6. Trong trường hợp một số cuộc gọi lại không bao giờ hoàn thành (e. g. cuộc gọi lại chứa một vòng lặp vô hạn), các cuộc gọi lại khác có thể không được thực thi. Trong những trường hợp này, một cuộc gọi lại có khả năng chặn phải sử dụng cấu trúc

    implicit val ec = ExecutionContext.global
    
    for (i <- 1 to 32000) {
      Future {
        blocking {
          Thread.sleep(999999)
        }
      }
    }
    
    9 (xem bên dưới)

  7. Sau khi được thực hiện, các cuộc gọi lại sẽ bị xóa khỏi đối tượng trong tương lai, do đó đủ điều kiện cho GC

Thành phần chức năng và khả năng hiểu

Cơ chế gọi lại mà chúng tôi đã chỉ ra là đủ để xâu chuỗi các kết quả trong tương lai với các tính toán tiếp theo. Tuy nhiên, nó đôi khi bất tiện và dẫn đến mã cồng kềnh. Chúng tôi hiển thị điều này với một ví dụ. Giả sử chúng tôi có API để giao tiếp với dịch vụ giao dịch tiền tệ. Giả sử chúng ta muốn mua đô la Mỹ, nhưng chỉ khi nó sinh lãi. Trước tiên, chúng tôi chỉ ra cách điều này có thể được thực hiện bằng cách sử dụng lệnh gọi lại

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
2

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
3

Chúng tôi bắt đầu bằng cách tạo một

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
58 tương lai có tỷ giá hối đoái hiện tại. Sau khi giá trị này được lấy từ máy chủ và tương lai hoàn tất thành công, quá trình tính toán sẽ tiếp tục trong cuộc gọi lại
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
4 và chúng tôi sẵn sàng quyết định có mua hay không. Do đó, chúng tôi tạo ra một
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
60 tương lai khác sẽ đưa ra quyết định mua chỉ khi làm như vậy có lợi, sau đó gửi yêu cầu. Cuối cùng, khi quá trình mua hàng hoàn tất, chúng tôi sẽ in thông báo thông báo ra đầu ra tiêu chuẩn

Điều này hoạt động, nhưng bất tiện vì hai lý do. Đầu tiên, chúng ta phải sử dụng

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
4 và lồng tương lai thứ hai của
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
60 vào bên trong nó. Hãy tưởng tượng rằng sau khi hoàn thành
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
60, chúng tôi muốn bán một số loại tiền tệ khác. Chúng tôi sẽ phải lặp lại mô hình này trong cuộc gọi lại
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
4, khiến mã bị thụt vào quá mức, cồng kềnh và khó lý giải

Thứ hai, tương lai

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
60 không nằm trong phạm vi của phần còn lại của mã– nó chỉ có thể được thực hiện từ bên trong cuộc gọi lại
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
4. Điều này có nghĩa là các phần khác của ứng dụng không nhìn thấy tương lai
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
60 và không thể đăng ký một cuộc gọi lại
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
4 khác cho nó, ví dụ: để bán một số loại tiền tệ khác

Vì hai lý do này, hợp đồng tương lai cung cấp các bộ kết hợp cho phép bố cục đơn giản hơn. Một trong những tổ hợp cơ bản là

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
69, được cung cấp một tương lai và chức năng ánh xạ cho giá trị của tương lai, tạo ra một tương lai mới được hoàn thành với giá trị được ánh xạ sau khi tương lai ban đầu được hoàn thành thành công. Bạn có thể lập luận về việc lập bản đồ tương lai giống như cách bạn lập luận về việc lập bản đồ các bộ sưu tập

Hãy viết lại ví dụ trước bằng cách sử dụng bộ kết hợp

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
69

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
4

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
5

Bằng cách sử dụng

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
69 trên
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
58, chúng tôi đã loại bỏ một cuộc gọi lại
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
4 và quan trọng hơn là lồng nhau. Nếu bây giờ chúng tôi quyết định bán một số loại tiền tệ khác, thì chỉ cần sử dụng lại
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
69 trên
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
60 là đủ

Nhưng điều gì sẽ xảy ra nếu

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
76 trả về
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
77, do đó gây ra một ngoại lệ bị ném? . Hơn nữa, hãy tưởng tượng rằng kết nối bị hỏng và
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
79 đã ném một ngoại lệ, không thành công
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
58. Trong trường hợp đó, chúng tôi sẽ không có giá trị để lập bản đồ, do đó,
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
60 sẽ tự động bị lỗi với cùng một ngoại lệ như
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
58

Tóm lại, nếu tương lai ban đầu được hoàn thành thành công thì tương lai được trả về sẽ được hoàn thành với giá trị được ánh xạ từ tương lai ban đầu. Nếu chức năng lập bản đồ đưa ra một ngoại lệ thì tương lai sẽ hoàn thành với ngoại lệ đó. Nếu tương lai ban đầu không thành công với một ngoại lệ thì tương lai được trả về cũng chứa ngoại lệ tương tự. Ngữ nghĩa lan truyền ngoại lệ này cũng có mặt trong phần còn lại của các bộ tổ hợp.

Một trong những mục tiêu thiết kế cho tương lai là cho phép sử dụng chúng để hiểu. Vì lý do này, hợp đồng tương lai cũng có các tổ hợp

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
3 và
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
84. Phương thức
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
3 nhận một hàm ánh xạ giá trị tới một tương lai mới
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
86, sau đó trả về một tương lai được hoàn thành sau khi hoàn thành
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
86

Giả sử rằng chúng ta muốn đổi đô la Mỹ lấy đồng franc Thụy Sĩ (CHF). Chúng tôi phải lấy báo giá cho cả hai loại tiền tệ và sau đó quyết định mua dựa trên cả hai báo giá. Dưới đây là một ví dụ về cách sử dụng

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
3 và
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
84 trong phạm vi hiểu

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
6

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
7

Tương lai

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
60 chỉ được hoàn thành khi cả hai
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
91 và
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
92 được hoàn thành– nó phụ thuộc vào giá trị của cả hai tương lai này nên quá trình tính toán của chính nó không thể bắt đầu sớm hơn

phần dễ hiểu ở trên được dịch sang

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
8

khó nắm bắt hơn một chút so với phần dễ hiểu, nhưng chúng tôi phân tích nó để hiểu rõ hơn về hoạt động

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
3. Hoạt động
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
3 ánh xạ giá trị của chính nó vào một số tương lai khác. Khi tương lai khác này được hoàn thành, tương lai kết quả được hoàn thành với giá trị của nó. Trong ví dụ của chúng tôi,
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
3 sử dụng giá trị của tương lai
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
91 để ánh xạ giá trị của
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
92 vào tương lai thứ ba gửi yêu cầu mua một lượng franc Thụy Sĩ nhất định. Tương lai kết quả
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
60 chỉ được hoàn thành khi tương lai thứ ba này được trả về từ
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
69 hoàn thành

Điều này có thể gây khó hiểu, nhưng may mắn thay, phép toán

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
3 hiếm khi được sử dụng bên ngoài để hiểu, dễ sử dụng và dễ hiểu hơn

Bộ kết hợp

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
5 tạo ra một tương lai mới chỉ chứa giá trị của tương lai ban đầu nếu nó thỏa mãn một số vị từ. Mặt khác, tương lai mới không thành công với một
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
02. Đối với các hợp đồng tương lai gọi
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
5 có tác dụng chính xác giống như việc gọi
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
84

Mối quan hệ giữa bộ kết hợp

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
05 và
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
5 tương tự như mối quan hệ của các phương thức này trong API bộ sưu tập

Vì đặc điểm

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 về mặt khái niệm có thể chứa hai loại giá trị (kết quả tính toán và ngoại lệ), nên cần có bộ kết hợp xử lý ngoại lệ

Giả sử rằng dựa trên

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
58, chúng tôi quyết định mua một số lượng nhất định. Phương pháp
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
09 cần một
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
10 để mua và dự kiến ​​là
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
11. Nó trả về số tiền đã mua. Nếu
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
11 đã thay đổi trong khi đó, nó sẽ ném một
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
13 và nó sẽ không mua bất cứ thứ gì. Nếu chúng tôi muốn tương lai của chúng tôi chứa
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
14 thay vì ngoại lệ, chúng tôi sử dụng bộ kết hợp
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
15

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
9

Bộ kết hợp

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
15 tạo ra một tương lai mới có cùng kết quả với tương lai ban đầu nếu nó hoàn thành thành công. Nếu không thì đối số hàm một phần được áp dụng cho
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 không thành công trong tương lai ban đầu. Nếu nó ánh xạ
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 tới một giá trị nào đó, thì tương lai mới sẽ được hoàn thành thành công với giá trị đó. Nếu chức năng một phần không được xác định trên
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 đó, thì tương lai kết quả sẽ thất bại với cùng một
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33

Bộ kết hợp

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
21 tạo ra một tương lai mới có cùng kết quả với tương lai ban đầu nếu nó hoàn thành thành công. Mặt khác, chức năng một phần được áp dụng cho
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 không thành công trong tương lai ban đầu. Nếu nó ánh xạ
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 tới một tương lai nào đó, thì tương lai này được hoàn thành với kết quả của tương lai đó. Mối quan hệ của nó với
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
15 tương tự như mối quan hệ của
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
3 với
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
69

Bộ kết hợp

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
27 tạo ra một tương lai mới chứa kết quả của tương lai này nếu nó được hoàn thành thành công hoặc nếu không thì kết quả thành công của đối số tương lai. Trong trường hợp cả tương lai này và tương lai đối số đều không thành công, thì tương lai mới được hoàn thành ngoại trừ tương lai này, như trong ví dụ sau cố gắng in giá trị đô la Mỹ, nhưng in giá trị đồng franc Thụy Sĩ trong trường hợp nó không thành công

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
0

Bộ kết hợp

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
28 được sử dụng hoàn toàn cho các mục đích tác dụng phụ. Nó trả về một tương lai mới với kết quả chính xác như tương lai hiện tại, bất kể tương lai hiện tại có thất bại hay không. Khi thì tương lai hiện tại được hoàn thành với kết quả, thì lần đóng tương ứng với
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
28 được gọi và sau đó thì tương lai mới được hoàn thành với kết quả tương tự như tương lai này. Điều này đảm bảo rằng nhiều cuộc gọi
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
28 được sắp xếp, như trong ví dụ sau lưu trữ các bài đăng gần đây từ mạng xã hội vào một tập hợp có thể thay đổi và sau đó hiển thị tất cả các bài đăng lên màn hình

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
1

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
2

Tóm lại, các tổ hợp trên tương lai hoàn toàn là chức năng. Mỗi tổ hợp trả về một tương lai mới có liên quan đến tương lai mà nó được bắt nguồn từ

dự đoán

Để kích hoạt tính năng dễ hiểu đối với kết quả được trả về dưới dạng ngoại lệ, hợp đồng tương lai cũng có các dự báo. Nếu tương lai ban đầu không thành công, phép chiếu

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
39 trả về một tương lai chứa giá trị loại
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33. Nếu tương lai ban đầu thành công, phép chiếu
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
39 thất bại với một
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
02. Sau đây là một ví dụ in ngoại lệ ra màn hình

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
3

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
4

for-hiểu trong ví dụ này được dịch sang

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
5

Bởi vì

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
07 không thành công ở đây, việc đóng cửa được đăng ký cho cuộc gọi lại
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
4 trên một
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
37 mới thành công. Ví dụ sau không in gì ra màn hình

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
6

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
7

Mở rộng tương lai

Hỗ trợ mở rộng API tương lai với các phương thức tiện ích bổ sung đã được lên kế hoạch. Điều này sẽ cho phép các khung bên ngoài cung cấp các tiện ích chuyên biệt hơn

chặn

Hợp đồng tương lai nói chung là không đồng bộ và không chặn các luồng thực thi cơ bản. Tuy nhiên, trong một số trường hợp nhất định, cần phải chặn. Chúng tôi phân biệt hai hình thức chặn luồng thực thi. gọi mã tùy ý chặn luồng từ bên trong tương lai và chặn từ bên ngoài một tương lai khác, đợi cho đến khi tương lai đó hoàn thành

Chặn bên trong một tương lai

Như đã thấy với

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7 toàn cầu, có thể thông báo cho một
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7 về một cuộc gọi chặn với cấu trúc
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
9. Tuy nhiên, việc thực hiện hoàn toàn theo quyết định của
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7. Trong khi một số
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
7 chẳng hạn như
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
6 triển khai
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
9 bằng phương tiện của một
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
45, thì một số ngữ cảnh thực thi chẳng hạn như nhóm luồng cố định

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
8

sẽ không làm gì cả, như thể hiện trong hình dưới đây

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
9

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}
0

Có tác dụng tương tự như

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}
1

Mã chặn cũng có thể đưa ra một ngoại lệ. Trong trường hợp này, ngoại lệ được chuyển tiếp đến người gọi

Chặn bên ngoài Tương lai

Như đã đề cập trước đó, việc ngăn chặn một tương lai không được khuyến khích vì lợi ích của hiệu suất và để ngăn chặn bế tắc. Gọi lại và tổ hợp trên tương lai là một cách ưa thích để sử dụng kết quả của họ. Tuy nhiên, việc chặn có thể cần thiết trong một số trường hợp nhất định và được hỗ trợ bởi API Futures and Promises

Trong ví dụ giao dịch tiền tệ ở trên, một nơi để chặn nằm ở cuối ứng dụng để đảm bảo rằng tất cả các hợp đồng tương lai đã được hoàn thành. Dưới đây là một ví dụ về cách chặn kết quả của một tương lai

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}
2

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}
3

Trong trường hợp tương lai không thành công, người gọi được chuyển tiếp ngoại lệ mà tương lai không thành công. Điều này bao gồm phép chiếu

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
39– việc chặn nó dẫn đến việc ném ra một
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
02 nếu tương lai ban đầu được hoàn thành thành công

Ngoài ra, gọi

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
48 đợi cho đến khi tương lai hoàn thành, nhưng không truy xuất kết quả của nó. Theo cách tương tự, gọi phương thức đó sẽ không ném ngoại lệ nếu tương lai không thành công

Đặc điểm

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 thực hiện đặc điểm
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
50 với các phương pháp
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
51 và
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
52. Các phương thức này không thể được gọi trực tiếp bởi máy khách– chúng chỉ có thể được gọi bởi bối cảnh thực thi

ngoại lệ

Khi tính toán không đồng bộ đưa ra các ngoại lệ chưa được xử lý, tương lai liên quan đến các tính toán đó sẽ không thành công. Hợp đồng tương lai không thành công lưu trữ một thể hiện của

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 thay vì giá trị kết quả. Các
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 cung cấp phương pháp chiếu
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
39, cho phép
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 này được coi là giá trị thành công của một
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 khác. Các trường hợp ngoại lệ đặc biệt sau đây được đối xử khác nhau

  1. given ExecutionContext = ...
    val inverseFuture : Future[Matrix] = Future {
      fatMatrix.inverse()
    } // execution context is implicitly passed
    
    58 – ngoại lệ này giữ một giá trị liên quan đến lợi nhuận. Thông thường, cấu trúc
    given ExecutionContext = ...
    val inverseFuture : Future[Matrix] = Future {
      fatMatrix.inverse()
    } // execution context is implicitly passed
    
    59 trong nội dung phương thức được dịch sang
    given ExecutionContext = ...
    val inverseFuture : Future[Matrix] = Future {
      fatMatrix.inverse()
    } // execution context is implicitly passed
    
    60 với ngoại lệ này. Thay vì giữ ngoại lệ này, giá trị liên quan được lưu trữ trong tương lai hoặc một lời hứa

  2. given ExecutionContext = ...
    val inverseFuture : Future[Matrix] = Future {
      fatMatrix.inverse()
    } // execution context is implicitly passed
    
    61 - được lưu trữ khi tính toán không thành công do một
    given ExecutionContext = ...
    val inverseFuture : Future[Matrix] = Future {
      fatMatrix.inverse()
    } // execution context is implicitly passed
    
    62,
    given ExecutionContext = ...
    val inverseFuture : Future[Matrix] = Future {
      fatMatrix.inverse()
    } // execution context is implicitly passed
    
    63 hoặc một
    given ExecutionContext = ...
    val inverseFuture : Future[Matrix] = Future {
      fatMatrix.inverse()
    } // execution context is implicitly passed
    
    64 chưa được xử lý. Trong trường hợp này,
    given ExecutionContext = ...
    val inverseFuture : Future[Matrix] = Future {
      fatMatrix.inverse()
    } // execution context is implicitly passed
    
    61 có ngoại lệ chưa được xử lý là nguyên nhân của nó. Cơ sở lý luận đằng sau điều này là để ngăn chặn việc lan truyền các ngoại lệ quan trọng và liên quan đến luồng điều khiển thường không được mã máy khách xử lý, đồng thời thông báo cho máy khách về việc tính toán không thành công trong tương lai.

Các ngoại lệ nghiêm trọng (như được xác định bởi

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
66) được viết lại trong chuỗi thực thi tính toán không đồng bộ không thành công. Điều này thông báo cho mã quản lý các luồng thực thi của sự cố và cho phép nó bị lỗi nhanh, nếu cần. Xem
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
66 để biết mô tả chính xác hơn về ngữ nghĩa

Cho đến nay, chúng tôi chỉ xem xét các đối tượng

import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 được tạo bởi các tính toán không đồng bộ bắt đầu bằng phương pháp
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2. Tuy nhiên, tương lai cũng có thể được tạo bằng lời hứa

Mặc dù tương lai được định nghĩa là một loại đối tượng giữ chỗ chỉ đọc được tạo cho một kết quả chưa tồn tại, nhưng một lời hứa có thể được coi là một vùng chứa chỉ định một lần, có thể ghi, hoàn thành một tương lai. Nghĩa là, một lời hứa có thể được sử dụng để hoàn thành thành công một tương lai có giá trị (bằng cách “hoàn thành” lời hứa) bằng cách sử dụng phương thức

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
70. Ngược lại, một lời hứa cũng có thể được sử dụng để hoàn thành một tương lai với một ngoại lệ, bằng cách thất hứa, sử dụng phương thức
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
71

Một lời hứa

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
72 hoàn thành tương lai được trả lại bởi
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
73. Tương lai này là cụ thể cho lời hứa
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
72. Tùy thuộc vào việc triển khai, có thể xảy ra trường hợp
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
75

Xem xét ví dụ về người sản xuất-người tiêu dùng sau đây, trong đó một phép tính tạo ra một giá trị và chuyển nó cho một phép tính khác tiêu thụ giá trị đó. Việc chuyển giá trị này được thực hiện bằng một lời hứa

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}
4

Ở đây, chúng tôi tạo một lời hứa và sử dụng phương pháp

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
76 của nó để có được
import scala.concurrent.Future
import scala.concurrent.blocking

Future {
  blocking {
    myLock.lock()
    // ...
  }
}
2 mà nó hoàn thành. Sau đó, chúng tôi bắt đầu hai tính toán không đồng bộ. Cái đầu tiên thực hiện một số tính toán, dẫn đến giá trị
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
78, sau đó được sử dụng để hoàn thành tương lai
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
07, bằng cách thực hiện lời hứa
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
72. Người thứ hai thực hiện một số tính toán, sau đó đọc kết quả
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
78 của thì tương lai hoàn thành
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
07. Lưu ý rằng
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
83 có thể nhận được kết quả trước khi tác vụ
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
84 hoàn thành việc thực thi phương thức
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
85

Như đã đề cập trước đây, các lời hứa có ngữ nghĩa chỉ định một lần. Như vậy, chúng có thể được hoàn thành chỉ một lần. Gọi

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
70 trên một lời hứa đã được hoàn thành (hoặc không thành công) sẽ tạo ra một
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
87

Ví dụ sau đây cho thấy cách thất bại trong một lời hứa

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}
5

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}
6

Ở đây,

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
84 tính toán kết quả trung gian
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
78 và kiểm tra xem nó có hợp lệ không. Trong trường hợp nó không hợp lệ, nó sẽ thất bại trong lời hứa bằng cách hoàn thành lời hứa
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
72 với một ngoại lệ. Trong trường hợp này, tương lai liên quan
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
07 không thành công. Mặt khác,
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
84 tiếp tục tính toán của nó, và cuối cùng hoàn thành tương lai
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
07 với một kết quả hợp lệ, bằng cách hoàn thành lời hứa
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
72

Lời hứa cũng có thể được hoàn thành bằng phương pháp

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
95 có giá trị tiềm năng
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
20– kết quả thất bại thuộc loại
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
40 hoặc kết quả thành công thuộc loại
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
18

Tương tự như

given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
70, gọi
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
71 và
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
95 trên một lời hứa đã được hoàn thành sẽ đưa ra một
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
87

Một đặc tính hay của các chương trình được viết bằng lời hứa với các hoạt động được mô tả cho đến nay và tương lai được tạo thành thông qua các hoạt động đơn nguyên mà không có tác dụng phụ là các chương trình này có tính xác định. Tất định ở đây có nghĩa là, với điều kiện là không có ngoại lệ nào được đưa vào chương trình, kết quả của chương trình (các giá trị được quan sát trong tương lai) sẽ luôn giống nhau, bất kể lịch trình thực hiện của chương trình song song

Trong một số trường hợp, khách hàng có thể chỉ muốn hoàn thành lời hứa nếu nó chưa được hoàn thành (e. g. , có một số yêu cầu HTTP đang được thực hiện từ một số tương lai khác nhau và khách hàng chỉ quan tâm đến phản hồi HTTP đầu tiên - tương ứng với tương lai đầu tiên để hoàn thành lời hứa). Vì những lý do này, các phương pháp

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
03,
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
04 và
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
05 tồn tại theo lời hứa. Khách hàng nên lưu ý rằng việc sử dụng các phương pháp này dẫn đến các chương trình không mang tính quyết định mà phụ thuộc vào lịch trình thực hiện

Phương pháp

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
06 hoàn thành lời hứa với một tương lai khác. Sau khi tương lai được hoàn thành, lời hứa cũng được hoàn thành với kết quả của tương lai đó. Chương trình sau in ra
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
07

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}
7

Khi thất hứa với một ngoại lệ, ba loại phụ của

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 được xử lý đặc biệt. Nếu
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 được sử dụng để thất hứa là một
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
10, thì lời hứa được hoàn thành với giá trị tương ứng. Nếu
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 được sử dụng để phá vỡ lời hứa là một trường hợp của
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
63,
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
62 hoặc
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
64, thì
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
33 được bao bọc như là nguyên nhân của một ________6__61 mới, đến lượt nó lại thất hứa

Sử dụng lời hứa, phương pháp

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
16 của tương lai và cấu trúc
given ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // execution context is implicitly passed
76, bạn có thể triển khai bất kỳ tổ hợp thành phần chức năng nào được mô tả trước đó. Giả sử bạn muốn triển khai một bộ kết hợp mới
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
19, lấy hai hợp đồng tương lai
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
07 và
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
86 và tạo ra một hợp đồng tương lai thứ ba được hoàn thành bởi
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
07 hoặc
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
86 (tùy theo điều kiện nào đến trước), nhưng chỉ với điều kiện là nó thành công

Đây là một ví dụ về cách thực hiện

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}
8

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin.*

// the following is equivalent to `given ExecutionContext = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean =
         try
           myLock.lock()
           // ...
         finally
           done = true
         true

       def isReleasable: Boolean = done
    }
  )
}
9

Lưu ý rằng trong triển khai này, nếu cả

implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
07 và
implicit val ec: ExecutionContext = ...
val inverseFuture : Future[Matrix] = Future {
  fatMatrix.inverse()
} // ec is implicitly passed
86 đều không thành công, thì
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
26 sẽ không bao giờ hoàn thành (có giá trị hoặc có ngoại lệ)

tiện ích

Để đơn giản hóa việc xử lý thời gian trong các ứng dụng đồng thời

implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
1 giới thiệu một sự trừu tượng hóa
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
28.
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
28 không được coi là một sự trừu tượng hóa thời gian chung khác. Nó được dùng với các thư viện đồng thời và nằm trong gói
implicit val ec = ExecutionContext.global

for (i <- 1 to 32000) {
  Future {
    blocking {
      Thread.sleep(999999)
    }
  }
}
1

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
28 là lớp cơ sở đại diện cho một khoảng thời gian. Nó có thể là hữu hạn hoặc vô hạn. Khoảng thời gian hữu hạn được biểu diễn bằng lớp
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
32, được xây dựng từ độ dài
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
33 và lớp
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
34. Thời hạn vô hạn, cũng kéo dài từ
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
28, chỉ tồn tại trong hai trường hợp,
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
36 và
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
37. Thư viện cũng cung cấp một số lớp con
import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
28 cho các mục đích chuyển đổi ngầm định và những lớp con này không nên được sử dụng

Tóm tắt

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
28 chứa các phương thức cho phép

  1. Chuyển đổi sang các đơn vị thời gian khác nhau (
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    40,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    41,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    42,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    43,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    44,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    45,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    46 và
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    47)
  2. So sánh thời lượng (
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    48,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    49,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    50 và
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    51)
  3. Các phép toán số học (_______30_______52,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    53,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    54,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    55 và
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    56)
  4. Tối thiểu và tối đa giữa thời lượng
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    57 và thời lượng được cung cấp trong đối số (
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    58,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    59)
  5. Kiểm tra xem thời lượng có hữu hạn hay không (
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    60)

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
28 có thể được khởi tạo theo các cách sau

  1. Ngầm định từ các loại
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    62 và
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    33, ví dụ,
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    64
  2. Bằng cách chuyển một chiều dài
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    33 và một
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    34, ví dụ:
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    67
  3. Bằng cách phân tích một chuỗi đại diện cho một khoảng thời gian, ví dụ:
    import scala.concurrent.{ Future, ExecutionContext }
    import scala.concurrent.forkjoin._
    
    // the following is equivalent to `implicit val ec = ExecutionContext.global`
    import ExecutionContext.Implicits.global
    
    Future {
      ForkJoinPool.managedBlock(
        new ManagedBlocker {
           var done = false
    
           def block(): Boolean = {
             try {
               myLock.lock()
               // ...
             } finally {
              done = true
             }
             true
           }
    
           def isReleasable: Boolean = done
        }
      )
    }
    
    68

Thời lượng cũng cung cấp các phương thức

import scala.concurrent.{ Future, ExecutionContext }
import scala.concurrent.forkjoin._

// the following is equivalent to `implicit val ec = ExecutionContext.global`
import ExecutionContext.Implicits.global

Future {
  ForkJoinPool.managedBlock(
    new ManagedBlocker {
       var done = false

       def block(): Boolean = {
         try {
           myLock.lock()
           // ...
         } finally {
          done = true
         }
         true
       }

       def isReleasable: Boolean = done
    }
  )
}
69, vì vậy nó có thể được sử dụng trong các cấu trúc đối sánh mẫu. ví dụ

Sự khác biệt giữa tương lai và lời hứa trong JavaScript là gì?

Cụ thể, khi cách sử dụng được phân biệt, một tương lai là dạng xem giữ chỗ chỉ đọc của một biến, trong khi một lời hứa là một vùng chứa gán đơn, có thể ghi, đặt giá trị của tương lai

Sự khác biệt giữa Tương lai và một lời hứa là gì?

Tương lai và lời hứa là những khái niệm khá giống nhau, điểm khác biệt là tương lai là vùng chứa chỉ đọc cho kết quả chưa tồn tại, trong khi lời hứa có thể được viết ( . .

Sự khác biệt giữa lời hứa Rust Future và JavaScript là gì?

Hợp đồng tương lai trong Rust không minh bạch như Hứa hẹn trong JavaScript . Đó là vì những gì mỗi ngôn ngữ đang cố gắng làm. JavaScript là ngôn ngữ cấp cao bao gồm vòng lặp thời gian chạy và sự kiện xử lý việc lên lịch tác vụ không đồng bộ và chạy lệnh gọi lại, khiến nhà phát triển ứng dụng chỉ sử dụng các tính năng này.

Những lời hứa vẫn được sử dụng trong JavaScript?

Lời hứa được sử dụng để xử lý các hoạt động không đồng bộ trong JavaScript. Chúng rất dễ quản lý khi xử lý nhiều hoạt động không đồng bộ trong đó các lệnh gọi lại có thể tạo ra địa ngục gọi lại dẫn đến mã không thể quản lý được.