ãã®è¨äºã¯ã¯ã¦ãªãããããã¼ã¢ããã³ãã«ã¬ã³ãã¼2015ã®16æ¥ç®ã®è¨äºã§ãï¼æ¨æ¥ã¯
id:motemen ã® エンジニア寿司を支える技術 - Hatena Developer Blog ã§ããï¼
ããã«ã¡ã¯ï¼
id:yashigani_w ã§ãï¼
ã¯ã¦ãªã§ã¯å®æçã«éçºå宿ãããï¼å¥½ããªéçºè¨èªã試ãããï¼æ®æ®µä»äºã§ã¯ä¸ç·ã«ãªããªãã¡ã³ãã¼ã¨ãµã¼ãã¹éçºããããã¨ãã§ãã¾ãï¼
ä»å¹´ã®å宿ã§ç§ãæå±ãããã¼ã ã§ã¯ï¼node.jsã¨typescriptã使ãï¼Webãµã¼ãã¹ãéçºãã¾ããï¼
ç§ã¯æ®æ®µiOSã¢ããªéçºãæ
å½ãã¦ããã®ã§ï¼ãã¾ããµã¼ããµã¤ãã®å®è£
ããããã¨ã¯ããã¾ãããï¼JavaScriptããã¾ã徿ã§ã¯ãªãã®ã§ããï¼éçºåå®¿ã®æ©ä¼ã使ã£ã¦æ°ããªæè¡ã«ææ¦ãã¦ã¿ã¾ããï¼
å宿ãåã«æè¡çã«ä¸å®ãæ±ãã¦ããç§ã¯ï¼ãããã¼ã ã¡ã³ãã¼ã«ãäºåã«ãªã«ãå¦ãã§ããã°ããããã¨è¨ªããã¨ããï¼ãPromiseããã使ãããçè§£ãã¦ããã¦ãããã¨å©è¨ããã¾ããï¼ Promiseã«ã¤ãã¦èª¿ã¹ã¯ãããã¨ãããã¯Swiftã§å®è£ ããã¨ããããããã ããã¨æãï¼å¦ç¿ãã¦ãSwiftã§å®è£ ãã¦ã¿ããã¨ã«ãã¾ããï¼
Promiseã¨ã¯
å®è£ ããããã«ã¯ï¼ã¾ãPromiseã«ã¤ãã¦çè§£ãæ·±ããå¿ è¦ãããã¾ãï¼ ããããPromiseã¨ã¯ãªããªã®ã§ããããï¼ Promiseã¯ï¼éåæå¦çãå å ãããªãã¸ã§ã¯ãã§ï¼ããã«å¯¾ãã¦å¦çã追å ãã¦ãããã¨ãã§ããã®ãç¹å¾´ã§ãï¼ ä»åã®å ãã¿ã¨ãªãJavaScriptã§ã¯ï¼Promiseã使ã£ã¦ãã®ãããªã³ã¼ããæ¸ããã¨ãã§ãã¾ãï¼
// http://azu.github.io/promises-book/#how-to-write-promiseããæç² function getURL(URL) { return new Promise(function (resolve, reject) { var req = new XMLHttpRequest(); req.open('GET', URL, true); req.onload = function () { if (req.status === 200) { resolve(req.responseText); } else { reject(new Error(req.statusText)); } }; req.onerror = function () { reject(new Error(req.statusText)); }; req.send(); }); } // å®è¡ä¾ var URL = "http://httpbin.org/get"; getURL(URL).then(function onFulfilled(value){ console.log(value); }).catch(function onRejected(error){ console.error(error); });
éåæå¦çããã¾ãé è½ããã¦ããããã«ï¼çµæã«å¯¾ããæä½ã鿬¡å®è¡ã®ããã«å ããäºãã§ãã¾ãï¼
ãã®Promiseã§ããï¼Promise - JavaScript | MDN ãåç
§ããã¨ï¼æå¤ã¨ããèªä½ã®APIã¯ãã»ã©å¤ãããã¾ããï¼
éåæå¦çãé è½ãããã¨ï¼ç¶æ
ã¨å¤ãæã¤ãã¨ï¼thenã¨catchã§å¥ã®Promiseãä½ã£ã¦è¿ããã¨ã®3ã¤ãæããã°å®è£
ã§ãããã§ãï¼
ä»åã¯ãã®ããã¥ã¡ã³ããåç
§ãã¤ã¤å®è£
ãã¦ã¿ã¾ããï¼
éåæå¦çãé è½ãã
Swiftã§éåæå¦çãããªãGCDã使ãã¾ãï¼
(Swiftã®OSSåã«ä¼´ãï¼ã³ã¢ã©ã¤ãã©ãªã¨ãã¦å
¬éãããã®ããã£ã¦ï¼å®å¿ãã¦ä½¿ãã¾ã)
ã¨ãã£ã¦ãï¼ãã®ã¾ã¾initã«ä¸ããããclosureãéåæå®è¡ããã ãã§ãï¼
public init(_ executor: (T -> Void, ErrorType -> Void) -> Void) { dispatch_async(queue, { executor(self.onFulfilled, self.onRejected) }) }
onFulfilledã¨onRejectedã¯å¾è¿°ããPromiseã®ç¶æ
ã夿´ããããã®ã¡ã½ããã§ãï¼
ç¶æ ã¨å¤
Promiseã«ã¯ Pending/fulfilled/rejected ã®3ã¤ã®ç¶æ ãããã¾ãï¼ å ãã¦ï¼fulfilled ã rejected ã®ç¶æ ã§ã¯éåæå¦çã®çµæã«ãã£ã¦å¾ãããå¤ãã¨ã©ã¼ãæã£ã¦ãã¾ãï¼ ããããSwiftã§ã¯enumã使ã£ã¦è¡¨ç¾ããã®ãããã§ãããï¼ å¤ã«ã¤ãã¦ã¯ä»»æã®åã使ããããã«ãããã®ã§ï¼ã¸ã§ããªã¯ã¹ã¨enumãçµã¿åããã¦ãã®ããã«è¡¨ç¾ãããã¨ã«ãã¾ããï¼
// ç¶æ enum State { case Pending case Fulfilled case Rejected } // çµæã®å¤ãã¨ã©ã¼ enum Result<T> { case Undefined case Value(T) case Error(ErrorType) }
ããã¦ããããæã¤Promiseãå®ç¾©ãã¾ãï¼
public final class Promise<T> { internal private(set) var state: State = .Pending internal private(set) var result: Result<T> = .Undefined public init(_ executor: (T -> Void, ErrorType -> Void) -> Void) { dispatch_async(queue, { executor(self.onFulfilled, self.onRejected) }) } private func onFulfilled(value: T) { if case .Pending = state { result = .Value(value) state = .Fulfilled } } private func onRejected(error: ErrorType) { if case .Pending = state { result = .Error(error) state = .Rejected } } }
onFulfilledãonRejectedã§ã¯è¤æ°åå®è¡ãããªãããã«.Pendingã®ã¨ãã®ã¿ç¶æ
ã夿´ããããã«ãã¦ãã¾ãï¼
ã¾ãï¼å¤é¨ããç¶æ
ã夿´ãããã¨å°ãé¨åã«é¢ãã¦ã¯privateã§ã¢ã¯ã»ã¹å¶å¾¡ããã¦ããã¾ãï¼
ããã§ï¼Tåã®å¤ãæã¤Promise<T>ãå®ç¾©ã§ãã¾ããï¼
thenã¨catch
thenã¨catchã¯Promiseã«å¦çã追å ãï¼æ°ããPromiseãè¿ãã¾ãï¼
catchã¯then(null, onRejcted)ã®ã·ã§ã¼ããã³ãã¨ã¿ãªããã¨ãã§ãã¾ãã®ã§ï¼å®è£
èªä½ã¯thenã®ã¿ãèããã°äºè¶³ãã¾ãï¼
å ã®Promiseã®éåæå¦çãçµäºãã¦ããå ´å(fulfilled ã¾ã㯠rejected ã§ããã¨ã)ã¯ï¼å³æã§ä¸ãããã颿°ãå®è¡ãã¦æ°ããPromiseãè¿ããã¨ãã§ãã¾ããï¼ããã§ãªãå ´å(pending ã®ç¶æ )ã§ã¯ï¼éåæå¦çã®å®äºãå¾ ã¤å¿ è¦ãããã¾ãï¼ ããã§ï¼å ã®Promiseã®å¦çã®å®äºãå¾ ã¤ããã«ï¼ããããã£ã«éåæå¦çã®å®äºã¨ã¨ãã«å®è¡ãããclosureã追å ãã¾ãï¼
public final class Promise<T> { internal private(set) var state: State = .Pending { didSet { if case .Pending = oldValue { switch (state, result) { case (.Fulfilled, .Value(let value)): resolve?(value) case (.Rejected, .Error(let error)): reject?(error) default: () } } } } private var resolve: (T -> Void)? private var reject: (ErrorType -> Void)? // çç¥... private func onFulfilled(value: T) { if case .Pending = state { result = .Value(value) state = .Fulfilled } } private func onRejected(error: ErrorType) { if case .Pending = state { result = .Error(error) state = .Rejected } } }
stateã.Pendingããå¤åããã¨ãã«å®è¡ãã¾ãï¼
thenã§ãããã®ããããã£ã«closureãã»ããããã°éåæå®è¡ãå¾
ã¤ãã¨ãã§ãã¾ãï¼
ã¾ãï¼ä»åã¯PromiseãPromise<T>ã¨ãã¾ãããï¼thenã®æä½ã¯Promise<T>ããPromise<U>ã«åãæä½ã ã¨ã¨ããããã¨ãã§ãã¾ãã®ã§ï¼thenã¯ãã®ããã«å®ç¾©ãã¾ãï¼
func then<U>(onFulfilled: T -> U, _ onRejected: (ErrorType -> Void)?) -> Promise<U> { return Promise<U> { _resolve, _reject in switch (self.state, self.result) { case (.Pending, _): let resolve = self.resolve self.resolve = { resolve?($0) _resolve(onFulfilled($0)) } let reject = self.reject self.reject = { reject?($0) _reject($0) onRejected?($0) } case (.Fulfilled, .Value(let value)): _resolve(onFulfilled(value)) case (.Rejected, .Error(let error)): _reject(error) onRejected?(error) default: assertionFailure() } } }
å ã®Promiseã®ç¶æ ã«ãã£ã¦å¦çãåå²ãã¦ãã¾ãï¼
thenãã§ããã®ã§ï¼ããã使ã£ã¦catchãå®è£
ã§ãã¾ãï¼
public func `catch`(onRejected: ErrorType -> Void) -> Promise<T> { return then({ $0 }, onRejected) }
ããã§ã¯thenã®ç¬¬ä¸å¼æ°ã«å¼æ°ããã®ã¾ã¾è¿ãclosureãæ¸¡ãã®ããã¤ã³ãã§ãï¼
ããã§åºæ¬çãªæ©è½ã¯å®è£ ã§ãã¾ããï¼
ãã®ä»ã®APIãå®è£ ãã
ES6ã®Promiseã«ã¯ä»ã«ãããã¤ãã®APIãããã¾ããï¼åºæ¬çã«ãã¾ã¾ã§å®è£
ãããã®ãçµã¿åãããã°å®è£
ãããã¨ãã§ãã¾ãï¼
ä¾ãã°ï¼Promise.resolveã§ã¯ãã®ããã«éåæå¦çã®å®è¡ãå¾
ã¡ã¾ãï¼
public static func resolve(value: T) -> Promise<T> { let semaphore = dispatch_semaphore_create(0) let promise = Promise { resolve, _ in resolve(value) dispatch_semaphore_signal(semaphore) } dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) return promise }
Swiftã£ã½ãå³ä»ãããã
ããã¾ã§ã¯ãªãã¹ãES6ã®Promiseã«åã£ãå®è£ ããã¾ãããï¼ãã£ããSwiftã§å®è£ ãã¦ããã®ã§å°ãSwiftã£ã½ããå¹ããããã¨æãã¾ãï¼
thenããªã¼ãã¼ãã¼ããã¦trailing closureã使ããããã«ãã
ES6ã§ã¯thenã¡ã½ããã¯p.then(onFulfilled, onRejected)ã®ããã«å®ç¾©ããã¦ãã¾ããï¼Swiftã§ã¯ãªã¼ãã¼ãã¼ãã1弿°ã®ãã®ã¨2弿°ã®ãã®ãåããã»ããããã§ãããï¼
// 2弿°then public func then<U>(onFulfilled: T -> U, _ onRejected: ErrorType -> Void) -> Promise<U> { } // 1弿°then public func then<U>(onFulfilled: T -> U) -> Promise<U> { }
Swiftã§ã¯ããã©ã«ã弿°ãä¸ãããã¨ã§ã¡ã½ããå¼ã³åºãæã®å¼æ°ãçç¥ã§ãã¾ãï¼
ãããï¼2弿°ã®onRejectedãOptionalã«ãããã©ã«ã弿°ã«nilãä¸ããã¹ã¿ã¤ã«ã ã¨ï¼trailing closure(弿°ã®æå¾ã®closureã¯ã¡ã½ããå¼ã³åºãã®å¤ã«æ¸ããã¨ãã§ããè¨æ³)ã使ãã¾ããï¼
// ããã©ã«ã弿°ã®å ´åtrailing cosureã使ããªã Promise.resolve(1) .then { $0 * 2 } .then { $0 + 1 }
ãã®ãããªãã¿ã¼ã³ã§ã¯ããã©ã«ã弿°ã§ã¯ãªãï¼ãªã¼ãã¼ãã¼ãã使ãã»ããè¯ãã¨ããã¾ãï¼
autoclosureã使ã£ã¦ä¾¿å©ã¤ãã·ã£ã©ã¤ã¶ã追å ãã
JSONã®ãã¼ã¹çµæãªã©ï¼ã¨ã©ã¼ãçºçããã颿°ã®çµæããã®ã¾ã¾Promiseã«å ã¿ãããªããã¨ãããããããã¾ããï¼ ãã®é½åº¦ï¼
let p = Promise { onFulfilled, onRejected in do { let obj = try NSJSONSerialization.JSONObjectWithData(data, options: []) onFulfilled(obj) } catch { onRejected(error) } }
ã®ããã«ã¨ã©ã¼ãã³ããªã³ã°ãæ¸ãã®ã¯é¢åã§ãï¼
ããã§ï¼autoclosureã使ã£ã¦ãã®ãããªã¤ãã·ã£ã©ã¤ã¶ã追å ãï¼ã¨ã©ã¼ãã³ããªã³ã°ãé è½ããã¨ä¾¿å©ã§ãï¼
public convenience init(@autoclosure(escaping) _ executor: () throws -> T) { self.init { resolve, reject in do { let v = try executor() resolve(v) } catch { reject(error) } } }
autoclosureãªå¼æ°ã¯ï¼èªåã§closureã«å
ã¾ãã¾ãï¼
escapingã¯ãã®closureã¯å³æã«å®è¡ãããã¨ããæå³ã§ï¼ãã®é¢æ°ãå¤ã®å¤ããã£ããã£ããªããã¨ã示ãã¾ãï¼
ãã®ä¾¿å©ã¤ãã·ã£ã©ã¤ã¶ã«ãã£ã¦ï¼å
ã®ã³ã¼ãã¯ãã®ãããªå½¢ã§Promiseã«å
ããã¨ãã§ããããã«ãªãã¾ãï¼
let p = Promise(try NSJSONSerialization.JSONObjectWithData(data, options: []))
autoclosure便å©ã§ããï¼
ã¾ã¨ã
æ°ããæ¦å¿µãå¦ç¿ããããã«å®éã«å®è£ ãã¦ã¿ãã®ã¯å¦ç¿å¹æãã¨ã¦ãé«ãï¼ãªã«ããæ¥½ãããã¨ãã§ãã¾ããï¼ å½åAPIã°ãããèªãã§å®è£ ãã¦ããã®ã§ããï¼åãç¡ãã¨ãã®æ¬è³ªãæããã®ãé£ããï¼ã¤ã³ã¿ã¼ãã§ã¼ã¹ããã°ããçè§£ããããã«åã¯æçãªæ å ±ãªãã ã¨åèªèããæ©ä¼ã«ããªãã¾ããï¼
æå¾ã«ç³ã訳ç¨åº¦ã«Swiftã£ã½ãå³ä»ãããã¦ã¿ã¾ãããï¼catchã¯äºç´èªãªã®ã§ä½¿ãã¨ãã«ã¨ã¹ã±ã¼ããå¿
è¦ãªã®ã§ãã¾ã好ã¾ããããã¾ããï¼
APIã«ã¤ãã¦ã¯ä¸èã®ä½å°ãããããã§ãï¼
ä»åç´¹ä»ããPromiseã¯ä»¥ä¸ã®ãªãã¸ããªã§å ¬éãã¦ãã¾ãï¼ ãã¹ãã³ã¼ã以ä¸ã®å©ç¨ä¾ã¯ããã¾ããã®ã§å¸¸ç¨ã«èããããã¾ã§ã¯ãããã¾ããï¼ ã¾ãï¼Promiseã®ä»æ§ã¯ promises-aplus/promises-spec · GitHub ã§çå®ããã¦ãã¾ããï¼ä»åã®å®è£ ã¯ãã¡ãã®å ¨ã¦ãç¶²ç¾ ãã¦ããããã§ã¯ãªããã¨ã«æ³¨æãã¦ãã ããï¼ CocoaPodsãCarthageãªã©ã®ããã±ã¼ã¸ããã¼ã¸ã£ã§ãã¤ã³ã¹ãã¼ã«ã§ãã¾ãã®ã§ï¼èå³ãããã°ä½¿ã£ã¦ã¿ã¦ãã ããï¼
徿¥è«
å宿ã§ã¯ä¸»ã«async/awaitã使ã£ãã®ã§ï¼Promiseãç´æ¥ä½¿ããã¨ã¯ã»ã¼ããã¾ããã§ããï¼
åèè³æ
- Promise - JavaScript | MDN
- JavaScript Promiseの本
- The Swift Programming Language (Swift 2.1): Closures
- promises-aplus/promises-spec · GitHub
ææ¥ã®ã¢ããã³ãã«ã¬ã³ãã¼ã¯
id:wtatsuru ã§ãï¼
ãæ¥½ãã¿ã«ï¼