Last active
May 2, 2018 22:07
-
-
Save dead-claudia/81c48a8fa458ef5832d065292ccd1f47 to your computer and use it in GitHub Desktop.
Proposed parallel async primitives, compared to callbacks + jQuery and RxJS + promises, using samples adapted from previous examples I found and used (I forget my sources...)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Non-blocking async iterators + Angular 1 | |
function searchWikipedia(term) { | |
return $http({ | |
url: "http://en.wikipedia.org/w/api.php?&callback=JSON_CALLBACK", | |
method: "jsonp", | |
params: { | |
action: "opensearch", | |
search: encodeURI(term), | |
format: "json" | |
} | |
}) | |
} | |
;(async () => { | |
const ready = throttle(1000) | |
let last | |
for await all (let value of observeOnScope($scope, "search")) { | |
if (!ready()) continue | |
value = value || "" | |
if (value !== last) { | |
last = value | |
const result = await searchWikipedia(value) | |
if (!safeApply($scope, () => $scope.data = result)) break | |
} | |
} | |
})() | |
// Utilities for above | |
function throttle(delay) { | |
let date = 0 | |
return () => { | |
const current = Date.now() | |
if (current < date) return false | |
date = current + delay | |
return true | |
} | |
} | |
function safeApply($scope, func) { | |
if ($scope.$$destroyed) return false | |
if ($scope.$$phase || $scope.$root.$$phase) { | |
func() | |
} else { | |
$scope.apply(() => { func() }) | |
} | |
return true | |
} | |
async function *observeOnScope($scope, expr) { | |
const resolves = [] | |
const values = [] | |
const sub = $scope.$watch(expr, value => { | |
if (resolves.length) resolves.pop()(value) | |
else values.push(value) | |
}) | |
try { | |
while (true) { | |
if (values.length) yield values.pop() | |
else yield new Promise(r => { resolves.push(r) }) | |
} | |
} finally { | |
sub.unsubscribe() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Non-blocking async iterators | |
function randInt(range) { | |
return Math.random() * range | 0 | |
} | |
async function *getSuggestions(selector) { | |
yield undefined | |
const response = await fetch(`https://api.github.com/users?since=${randInt(500)}`) | |
const listUsers = await response.json() | |
let current = listUsers[randInt(listUsers.length)] | |
yield current | |
// In stream generators, only literal `await value` expressions are deferred. | |
await all { | |
for await all (const _ of fromEvent(document.querySelector(".refresh"), "click")) { | |
yield current = undefined | |
const response = await fetch(`https://api.github.com/users?since=${randInt(500)}`) | |
const listUsers = await response.json() | |
yield current = listUsers[randInt(listUsers.length)] | |
} | |
for await all (const _ of fromEvent(document.querySelector(selector), 'click')) { | |
yield current | |
} | |
} | |
} | |
;(async () => { | |
for all (const selector of [".close1", ".close2", ".close3"]) { | |
for await all (const suggestion of getSuggestions(selector)) { | |
if (suggestion == null) { | |
// hide the first suggestion DOM element | |
} else { | |
// show the first suggestion DOM element and render the data | |
} | |
} | |
} | |
})() | |
// Helper | |
async function *fromEvent(elem, name) { | |
const resolves = [] | |
const values = [] | |
function listener(e) { | |
if (resolves.length) resolves.pop()(e) | |
else values.push(e) | |
} | |
elem.addEventListener(name, listener, false) | |
try { | |
while (true) { | |
if (values.length) yield values.pop() | |
else yield new Promise(r => { resolves.push(r) }) | |
} | |
} finally { | |
elem.removeEventListener(name, listener, false) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Callbacks + Angular 1 | |
function searchWikipedia(term, next) { | |
return $http({ | |
url: "http://en.wikipedia.org/w/api.php?&callback=JSON_CALLBACK", | |
method: "jsonp", | |
params: { | |
action: "opensearch", | |
search: encodeURI(term), | |
format: "json" | |
} | |
}) | |
.then(next) | |
} | |
var last | |
var sub = $scope.$watch(expr, throttle(function (value) { | |
value = value || "" | |
if (value !== last) { | |
last = value | |
searchWikipedia(value, safeApply($scope, sub, function (result) { | |
$scope.data = result | |
})) | |
} | |
})) | |
// Utilities for above | |
function throttle(delay, func) { | |
let date = 0 | |
return function () { | |
const current = Date.now() | |
if (current < date) return | |
date = current + delay | |
func.apply(this, arguments) | |
} | |
} | |
function safeApply($scope, sub, func) { | |
return function (value) { | |
if ($scope.$$destroyed) { | |
sub.unsubscribe() | |
} else if ($scope.$$phase || $scope.$root.$$phase) { | |
func(value) | |
} else { | |
$scope.apply(function () { func(value) }) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Callbacks + jQuery | |
function randInt(range) { | |
return Math.random() * range | 0 | |
} | |
function getSuggestions(selector, send) { | |
send(undefined) | |
function getUsers(next) { | |
$.ajax({url: "https://api.github.com/users?since=" + randInt(500)}) | |
.fail(function (e) { console.error(e) }) | |
.done(function (listUsers) { next(listUsers) }) | |
} | |
getUsers(function (listUsers) { | |
var current = listUsers[randInt(listUsers.length)] | |
send(current) | |
$(".refresh").click(function () { | |
current = undefined | |
send(undefined) | |
getUsers(function (listUsers) { | |
current = listUsers[randInt(listUsers.length)] | |
send(current) | |
}) | |
}) | |
$(selector).click(function () { | |
send(current) | |
}) | |
}) | |
} | |
;[".close1", ".close2", ".close3"].forEach(function (selector) { | |
getSuggestions(selector, function (suggestion) { | |
if (suggestion == null) { | |
// hide the first suggestion DOM element | |
} else { | |
// show the first suggestion DOM element and render the data | |
} | |
}) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// RxJS Observables + Angular 1 | |
import {Observable} from "rxjs" | |
function searchWikipedia(term) { | |
return Observable.fromPromise($http({ | |
url: "http://en.wikipedia.org/w/api.php?&callback=JSON_CALLBACK", | |
method: "jsonp", | |
params: { | |
action: "opensearch", | |
search: encodeURI(term), | |
format: "json" | |
} | |
})) | |
} | |
safeApply( | |
$scope, | |
observeOnScope($scope, "search") | |
.throttle(1000) | |
.map(value => value || "") | |
.distinctUntilChanged() | |
.flatMapLatest(searchWikipedia) | |
result => { $scope.data = result } | |
).subscribe() | |
// Utilities for above | |
function safeApply($scope, stream, func) { | |
return stream | |
.takeWhile(() => !$scope.$$destroyed) | |
.tap(data => { | |
if ($scope.$$phase || $scope.$root.$$phase) { | |
func(data) | |
} else { | |
$scope.apply(() => { func(data) }) | |
} | |
}) | |
} | |
function observeOnScope($scope, expr) { | |
return new Observable(observer => { | |
return $scope.$watch(expr, value => observer.next(value)) | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// RxJS Observables | |
import {Observable} from "rxjs" | |
function randInt(range) { | |
return Math.random() * range | 0 | |
} | |
function getSuggestions(selector) { | |
const refreshElem = document.querySelector(".refresh") | |
const baseElem = document.querySelector(selector) | |
const refreshClickStream = Rx.Observable.fromEvent(refreshElem, "click") | |
const responseStream = refreshClickStream.startWith() | |
.map(() => `https://api.github.com/users?since=${randInt(500)}`) | |
.flatMap(url => Rx.Observable.fromPromise( | |
window.fetch(url).then(response => response.json()) | |
)) | |
.map(() => listUsers[randInt(listUsers.length)]) | |
return Rx.Observable.fromEvent(baseElem, "click") | |
.startWith(undefined) | |
.combineLatest(responseStream, (_, listUsers) => listUsers) | |
.merge(refreshClickStream.map(() => undefined).startWith(undefined)) | |
.map(suggestion => ({selector, suggestion})) | |
} | |
Rx.Observable.of(".close1", ".close2", ".close3") | |
.flatMap(selector => getSuggestions(selector)) | |
.subscribe(({selector, suggestion}) => { | |
if (suggestion == null) { | |
// hide the selector's suggestion DOM element | |
} else { | |
// show the selector's suggestion DOM element and render the data | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment