mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
Implement fetch
This commit is contained in:
parent
ef00cf3e38
commit
a831d1e239
17 changed files with 330 additions and 10 deletions
4
Makefile
4
Makefile
|
@ -1,5 +1,6 @@
|
||||||
TS_FILES = \
|
TS_FILES = \
|
||||||
dispatch.ts \
|
dispatch.ts \
|
||||||
|
fetch.ts \
|
||||||
globals.ts \
|
globals.ts \
|
||||||
main.ts \
|
main.ts \
|
||||||
msg.pb.d.ts \
|
msg.pb.d.ts \
|
||||||
|
@ -18,8 +19,9 @@ GO_FILES = \
|
||||||
assets.go \
|
assets.go \
|
||||||
deno_dir.go \
|
deno_dir.go \
|
||||||
deno_dir_test.go \
|
deno_dir_test.go \
|
||||||
echo.go \
|
|
||||||
dispatch.go \
|
dispatch.go \
|
||||||
|
echo.go \
|
||||||
|
fetch.go \
|
||||||
main.go \
|
main.go \
|
||||||
msg.pb.go \
|
msg.pb.go \
|
||||||
os.go \
|
os.go \
|
||||||
|
|
|
@ -66,6 +66,7 @@ func Sub(channel string, cb Subscriber) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Pub(channel string, payload []byte) {
|
func Pub(channel string, payload []byte) {
|
||||||
|
wg.Add(1)
|
||||||
resChan <- &BaseMsg{
|
resChan <- &BaseMsg{
|
||||||
Channel: channel,
|
Channel: channel,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
|
@ -79,11 +80,14 @@ func PubMsg(channel string, msg *Msg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func DispatchLoop() {
|
func DispatchLoop() {
|
||||||
|
// runtime.LockOSThread()
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
first := true
|
first := true
|
||||||
|
|
||||||
// In a goroutine, we wait on for all goroutines to complete (for example
|
// In a goroutine, we wait on for all goroutines to complete (for example
|
||||||
// timers). We use this to signal to the main thread to exit.
|
// timers). We use this to signal to the main thread to exit.
|
||||||
|
// wg.Add(1) basically translates to uv_ref, if this was Node.
|
||||||
|
// wg.Done() basically translates to uv_unref
|
||||||
go func() {
|
go func() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
doneChan <- true
|
doneChan <- true
|
||||||
|
@ -92,7 +96,11 @@ func DispatchLoop() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case msg := <-resChan:
|
case msg := <-resChan:
|
||||||
|
wg.Done()
|
||||||
out, err := proto.Marshal(msg)
|
out, err := proto.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
err = worker.SendBytes(out)
|
err = worker.SendBytes(out)
|
||||||
stats.v8workerSend++
|
stats.v8workerSend++
|
||||||
stats.v8workerBytesSent += len(out)
|
stats.v8workerBytesSent += len(out)
|
||||||
|
|
14
dispatch.ts
14
dispatch.ts
|
@ -3,6 +3,7 @@ import { _global } from "./globals";
|
||||||
import { main as pb } from "./msg.pb";
|
import { main as pb } from "./msg.pb";
|
||||||
|
|
||||||
type MessageCallback = (msg: Uint8Array) => void;
|
type MessageCallback = (msg: Uint8Array) => void;
|
||||||
|
//type MessageStructCallback = (msg: pb.IMsg) => void;
|
||||||
|
|
||||||
const send = V8Worker2.send;
|
const send = V8Worker2.send;
|
||||||
const channels = new Map<string, MessageCallback[]>();
|
const channels = new Map<string, MessageCallback[]>();
|
||||||
|
@ -16,6 +17,19 @@ export function sub(channel: string, cb: MessageCallback): void {
|
||||||
subscribers.push(cb);
|
subscribers.push(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
export function subMsg(channel: string, cb: MessageStructCallback): void {
|
||||||
|
sub(channel, (payload: Uint8Array) => {
|
||||||
|
const msg = pb.Msg.decode(payload);
|
||||||
|
if (msg.error != null) {
|
||||||
|
f.onError(new Error(msg.error));
|
||||||
|
} else {
|
||||||
|
cb(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
export function pub(channel: string, payload: Uint8Array): null | ArrayBuffer {
|
export function pub(channel: string, payload: Uint8Array): null | ArrayBuffer {
|
||||||
const msg = pb.BaseMsg.fromObject({ channel, payload });
|
const msg = pb.BaseMsg.fromObject({ channel, payload });
|
||||||
const ui8 = pb.BaseMsg.encode(msg).finish();
|
const ui8 = pb.BaseMsg.encode(msg).finish();
|
||||||
|
|
64
fetch.go
Normal file
64
fetch.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitFetch() {
|
||||||
|
Sub("fetch", func(buf []byte) []byte {
|
||||||
|
msg := &Msg{}
|
||||||
|
check(proto.Unmarshal(buf, msg))
|
||||||
|
switch msg.Command {
|
||||||
|
case Msg_FETCH_REQ:
|
||||||
|
return Fetch(
|
||||||
|
msg.FetchReqId,
|
||||||
|
msg.FetchReqUrl)
|
||||||
|
default:
|
||||||
|
panic("[fetch] Unexpected message " + string(buf))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fetch(id int32, targetUrl string) []byte {
|
||||||
|
logDebug("Fetch %d %s", id, targetUrl)
|
||||||
|
async(func() {
|
||||||
|
resMsg := &Msg{
|
||||||
|
Command: Msg_FETCH_RES,
|
||||||
|
FetchResId: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Get(targetUrl)
|
||||||
|
if err != nil {
|
||||||
|
resMsg.Error = err.Error()
|
||||||
|
PubMsg("fetch", resMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp == nil {
|
||||||
|
resMsg.Error = "resp is nil "
|
||||||
|
PubMsg("fetch", resMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resMsg.FetchResStatus = int32(resp.StatusCode)
|
||||||
|
logDebug("fetch success %d %s", resMsg.FetchResStatus, targetUrl)
|
||||||
|
PubMsg("fetch", resMsg)
|
||||||
|
|
||||||
|
// Now we read the body and send another message0
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if resp == nil {
|
||||||
|
resMsg.Error = "resp is nil "
|
||||||
|
PubMsg("fetch", resMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resMsg.FetchResBody = body
|
||||||
|
PubMsg("fetch", resMsg)
|
||||||
|
|
||||||
|
// TODO streaming.
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
151
fetch.ts
Normal file
151
fetch.ts
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
import { assert, log, createResolvable, Resolvable } from "./util";
|
||||||
|
import * as util from "./util";
|
||||||
|
import * as dispatch from "./dispatch";
|
||||||
|
import { main as pb } from "./msg.pb";
|
||||||
|
import { TextDecoder } from "text-encoding";
|
||||||
|
|
||||||
|
export function initFetch() {
|
||||||
|
dispatch.sub("fetch", (payload: Uint8Array) => {
|
||||||
|
const msg = pb.Msg.decode(payload);
|
||||||
|
assert(msg.command === pb.Msg.Command.FETCH_RES);
|
||||||
|
const id = msg.fetchResId;
|
||||||
|
const f = fetchRequests.get(id);
|
||||||
|
assert(f != null, `Couldn't find FetchRequest id ${id}`);
|
||||||
|
|
||||||
|
f.onMsg(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchRequests = new Map<number, FetchRequest>();
|
||||||
|
|
||||||
|
class FetchResponse implements Response {
|
||||||
|
readonly url: string;
|
||||||
|
body: null;
|
||||||
|
bodyUsed = false; // TODO
|
||||||
|
status: number;
|
||||||
|
statusText = "FIXME"; // TODO
|
||||||
|
readonly type = "basic"; // TODO
|
||||||
|
redirected = false; // TODO
|
||||||
|
headers: null; // TODO
|
||||||
|
//private bodyChunks: Uint8Array[] = [];
|
||||||
|
private first = true;
|
||||||
|
|
||||||
|
constructor(readonly req: FetchRequest) {
|
||||||
|
this.url = req.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyWaiter: Resolvable<ArrayBuffer>;
|
||||||
|
arrayBuffer(): Promise<ArrayBuffer> {
|
||||||
|
this.bodyWaiter = createResolvable();
|
||||||
|
return this.bodyWaiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
blob(): Promise<Blob> {
|
||||||
|
throw Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
formData(): Promise<FormData> {
|
||||||
|
throw Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
async json(): Promise<object> {
|
||||||
|
const text = await this.text();
|
||||||
|
return JSON.parse(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
async text(): Promise<string> {
|
||||||
|
const ab = await this.arrayBuffer();
|
||||||
|
const enc = new TextDecoder("utf-8");
|
||||||
|
// Maybe new Uint8Array(ab)
|
||||||
|
return enc.decode(ab);
|
||||||
|
}
|
||||||
|
|
||||||
|
get ok(): boolean {
|
||||||
|
return 200 <= this.status && this.status < 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): Response {
|
||||||
|
throw Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
onHeader: (res: Response) => void;
|
||||||
|
onError: (error: Error) => void;
|
||||||
|
|
||||||
|
onMsg(msg: pb.Msg) {
|
||||||
|
if (msg.error !== null && msg.error !== "") {
|
||||||
|
//throw new Error(msg.error)
|
||||||
|
this.onError(new Error(msg.error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.first) {
|
||||||
|
this.first = false;
|
||||||
|
this.status = msg.fetchResStatus;
|
||||||
|
this.onHeader(this);
|
||||||
|
} else {
|
||||||
|
// Body message. Assuming it all comes in one message now.
|
||||||
|
const ab = util.typedArrayToArrayBuffer(msg.fetchResBody);
|
||||||
|
this.bodyWaiter.resolve(ab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextFetchId = 0;
|
||||||
|
//TODO implements Request
|
||||||
|
class FetchRequest {
|
||||||
|
private readonly id: number;
|
||||||
|
response: FetchResponse;
|
||||||
|
constructor(readonly url: string) {
|
||||||
|
this.id = nextFetchId++;
|
||||||
|
fetchRequests.set(this.id, this);
|
||||||
|
this.response = new FetchResponse(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsg(msg: pb.Msg) {
|
||||||
|
this.response.onMsg(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
fetchRequests.delete(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
log("dispatch FETCH_REQ", this.id, this.url);
|
||||||
|
const res = dispatch.sendMsg("fetch", {
|
||||||
|
command: pb.Msg.Command.FETCH_REQ,
|
||||||
|
fetchReqId: this.id,
|
||||||
|
fetchReqUrl: this.url
|
||||||
|
});
|
||||||
|
assert(res == null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetch(
|
||||||
|
input?: Request | string,
|
||||||
|
init?: RequestInit
|
||||||
|
): Promise<Response> {
|
||||||
|
const fetchReq = new FetchRequest(input as string);
|
||||||
|
const response = fetchReq.response;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
response.onHeader = (response: any) => {
|
||||||
|
log("onHeader");
|
||||||
|
resolve(response);
|
||||||
|
};
|
||||||
|
response.onError = (error: Error) => {
|
||||||
|
log("onError", error);
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
fetchReq.start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fetch('http://example.com/movies.json')
|
||||||
|
.then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function(myJson) {
|
||||||
|
console.log(myJson);
|
||||||
|
});
|
||||||
|
*/
|
|
@ -45,3 +45,10 @@ function stringifyArgs(args: any[]): string {
|
||||||
}
|
}
|
||||||
return out.join(" ");
|
return out.join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { fetch } from "./fetch";
|
||||||
|
_global["fetch"] = fetch;
|
||||||
|
|
||||||
|
import { TextEncoder, TextDecoder } from "text-encoding";
|
||||||
|
_global["TextEncoder"] = TextEncoder;
|
||||||
|
_global["TextDecoder"] = TextDecoder;
|
||||||
|
|
2
main.go
2
main.go
|
@ -24,6 +24,7 @@ func FlagsParse() []string {
|
||||||
if *flagV8Options {
|
if *flagV8Options {
|
||||||
args = append(args, "--help")
|
args = append(args, "--help")
|
||||||
}
|
}
|
||||||
|
args = append(args, "--abort-on-uncaught-exception")
|
||||||
args = v8worker2.SetFlags(args)
|
args = v8worker2.SetFlags(args)
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
@ -49,6 +50,7 @@ func main() {
|
||||||
InitOS()
|
InitOS()
|
||||||
InitEcho()
|
InitEcho()
|
||||||
InitTimers()
|
InitTimers()
|
||||||
|
InitFetch()
|
||||||
|
|
||||||
main_js := stringAsset("main.js")
|
main_js := stringAsset("main.js")
|
||||||
err := worker.Load("/main.js", main_js)
|
err := worker.Load("/main.js", main_js)
|
||||||
|
|
3
main.ts
3
main.ts
|
@ -7,8 +7,8 @@ import { main as pb } from "./msg.pb";
|
||||||
import * as runtime from "./runtime";
|
import * as runtime from "./runtime";
|
||||||
import * as util from "./util";
|
import * as util from "./util";
|
||||||
|
|
||||||
// These have top-level functions that need to execute.
|
|
||||||
import { initTimers } from "./timers";
|
import { initTimers } from "./timers";
|
||||||
|
import { initFetch } from "./fetch";
|
||||||
|
|
||||||
// To control internal logging output
|
// To control internal logging output
|
||||||
// Set with the -debug command-line flag.
|
// Set with the -debug command-line flag.
|
||||||
|
@ -32,6 +32,7 @@ dispatch.sub("start", (payload: Uint8Array) => {
|
||||||
util.log("start", { cwd, argv, debugFlag });
|
util.log("start", { cwd, argv, debugFlag });
|
||||||
|
|
||||||
initTimers();
|
initTimers();
|
||||||
|
initFetch();
|
||||||
runtime.setup(mainJs, mainMap);
|
runtime.setup(mainJs, mainMap);
|
||||||
|
|
||||||
const inputFn = argv[0];
|
const inputFn = argv[0];
|
||||||
|
|
20
msg.proto
20
msg.proto
|
@ -7,8 +7,6 @@ message BaseMsg {
|
||||||
}
|
}
|
||||||
|
|
||||||
message Msg {
|
message Msg {
|
||||||
string error = 1;
|
|
||||||
|
|
||||||
enum Command {
|
enum Command {
|
||||||
ERROR = 0;
|
ERROR = 0;
|
||||||
START = 1;
|
START = 1;
|
||||||
|
@ -19,8 +17,10 @@ message Msg {
|
||||||
TIMER_START = 6;
|
TIMER_START = 6;
|
||||||
TIMER_READY = 7;
|
TIMER_READY = 7;
|
||||||
TIMER_CLEAR = 8;
|
TIMER_CLEAR = 8;
|
||||||
|
FETCH_REQ = 9;
|
||||||
|
FETCH_RES = 10;
|
||||||
}
|
}
|
||||||
Command command = 2;
|
Command command = 1;
|
||||||
|
|
||||||
// We avoid creating a message for each command (and use oneof or any types)
|
// We avoid creating a message for each command (and use oneof or any types)
|
||||||
// In order to reduce code in the size of the generated javascript
|
// In order to reduce code in the size of the generated javascript
|
||||||
|
@ -28,6 +28,9 @@ message Msg {
|
||||||
// potentially add many hundreds of commands. Therefore we just prefix command
|
// potentially add many hundreds of commands. Therefore we just prefix command
|
||||||
// arguments by their name.
|
// arguments by their name.
|
||||||
|
|
||||||
|
// ERROR
|
||||||
|
string error = 2;
|
||||||
|
|
||||||
// START
|
// START
|
||||||
string start_cwd = 10;
|
string start_cwd = 10;
|
||||||
repeated string start_argv = 11;
|
repeated string start_argv = 11;
|
||||||
|
@ -67,4 +70,15 @@ message Msg {
|
||||||
|
|
||||||
// TIMER_CLEAR
|
// TIMER_CLEAR
|
||||||
int32 timer_clear_id = 80;
|
int32 timer_clear_id = 80;
|
||||||
|
|
||||||
|
// FETCH_REQ
|
||||||
|
int32 fetch_req_id = 90;
|
||||||
|
string fetch_req_url = 91;
|
||||||
|
// repeated string fetch_req_header_line = 91
|
||||||
|
|
||||||
|
// FETCH_RES
|
||||||
|
int32 fetch_res_id = 100;
|
||||||
|
int32 fetch_res_status = 101;
|
||||||
|
repeated string fetch_res_header_line = 102;
|
||||||
|
bytes fetch_res_body = 103;
|
||||||
}
|
}
|
||||||
|
|
4
os.go
4
os.go
|
@ -37,7 +37,7 @@ func InitOS() {
|
||||||
func ResolveModule(moduleSpecifier string, containingFile string) (
|
func ResolveModule(moduleSpecifier string, containingFile string) (
|
||||||
moduleName string, filename string, err error) {
|
moduleName string, filename string, err error) {
|
||||||
|
|
||||||
logDebug("ResolveModule %s %s", moduleSpecifier, containingFile)
|
logDebug("os.go ResolveModule moduleSpecifier %s containingFile %s", moduleSpecifier, containingFile)
|
||||||
|
|
||||||
moduleUrl, err := url.Parse(moduleSpecifier)
|
moduleUrl, err := url.Parse(moduleSpecifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,7 +76,7 @@ func HandleCodeFetch(moduleSpecifier string, containingFile string) (out []byte)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logDebug("HandleCodeFetch moduleSpecifier %s containingFile %s filename %s",
|
logDebug("CodeFetch moduleSpecifier %s containingFile %s filename %s",
|
||||||
moduleSpecifier, containingFile, filename)
|
moduleSpecifier, containingFile, filename)
|
||||||
|
|
||||||
if isRemote(moduleName) {
|
if isRemote(moduleName) {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/base64-js": "^1.2.5",
|
"@types/base64-js": "^1.2.5",
|
||||||
"@types/source-map-support": "^0.4.0",
|
"@types/source-map-support": "^0.4.0",
|
||||||
|
"@types/text-encoding": "^0.0.32",
|
||||||
"babel-polyfill": "^6.26.0",
|
"babel-polyfill": "^6.26.0",
|
||||||
"base64-js": "^1.3.0",
|
"base64-js": "^1.3.0",
|
||||||
"espree": "^3.5.3",
|
"espree": "^3.5.3",
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
"prettier": "^1.12.1",
|
"prettier": "^1.12.1",
|
||||||
"protobufjs": "^6.8.6",
|
"protobufjs": "^6.8.6",
|
||||||
"source-map": "0.6.0",
|
"source-map": "0.6.0",
|
||||||
|
"text-encoding": "^0.6.4",
|
||||||
"tmp": "0.0.33",
|
"tmp": "0.0.33",
|
||||||
"tslint": "5.10.0",
|
"tslint": "5.10.0",
|
||||||
"typescript": "^2.8.3",
|
"typescript": "^2.8.3",
|
||||||
|
|
|
@ -24,7 +24,7 @@ type AmdFactory = (...args: any[]) => undefined | object;
|
||||||
type AmdDefine = (deps: string[], factory: AmdFactory) => void;
|
type AmdDefine = (deps: string[], factory: AmdFactory) => void;
|
||||||
|
|
||||||
// Uncaught exceptions are sent to window.onerror by v8worker2.
|
// Uncaught exceptions are sent to window.onerror by v8worker2.
|
||||||
window.onerror = function(message, source, lineno, colno, error) {
|
window.onerror = (message, source, lineno, colno, error) => {
|
||||||
console.log(error.message, error.stack);
|
console.log(error.message, error.stack);
|
||||||
os.exit(1);
|
os.exit(1);
|
||||||
};
|
};
|
||||||
|
@ -143,11 +143,11 @@ export function resolveModule(
|
||||||
moduleSpecifier,
|
moduleSpecifier,
|
||||||
containingFile
|
containingFile
|
||||||
);
|
);
|
||||||
if (sourceCode.length == 0) {
|
if (sourceCode.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
util.log("resolveModule sourceCode length ", sourceCode.length);
|
util.log("resolveModule sourceCode length ", sourceCode.length);
|
||||||
let m = FileModule.load(filename);
|
const m = FileModule.load(filename);
|
||||||
if (m != null) {
|
if (m != null) {
|
||||||
return m;
|
return m;
|
||||||
} else {
|
} else {
|
||||||
|
|
12
testdata/fetch.ts
vendored
Normal file
12
testdata/fetch.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
const request = async () => {
|
||||||
|
const response = await fetch('http://localhost:4545/package.json');
|
||||||
|
const json = await response.json();
|
||||||
|
console.log("expect deno:", json.name);
|
||||||
|
if (json.name !== "deno") {
|
||||||
|
throw Error("bad value" + json.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request();
|
||||||
|
console.log("fetch started");
|
2
testdata/fetch.ts.out
vendored
Normal file
2
testdata/fetch.ts.out
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
fetch started
|
||||||
|
expect deno: deno
|
8
util.go
8
util.go
|
@ -49,3 +49,11 @@ func exitOnError(err error) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func async(cb func()) {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
cb()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
25
util.ts
25
util.ts
|
@ -20,3 +20,28 @@ export function typedArrayToArrayBuffer(ta: TypedArray): ArrayBuffer {
|
||||||
const ab = ta.buffer.slice(ta.byteOffset, ta.byteOffset + ta.byteLength);
|
const ab = ta.buffer.slice(ta.byteOffset, ta.byteOffset + ta.byteLength);
|
||||||
return ab as ArrayBuffer;
|
return ab as ArrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A `Resolvable` is a Promise with the `reject` and `resolve` functions
|
||||||
|
// placed as methods on the promise object itself. It allows you to do:
|
||||||
|
//
|
||||||
|
// const p = createResolvable<number>();
|
||||||
|
// ...
|
||||||
|
// p.resolve(42);
|
||||||
|
//
|
||||||
|
// It'd be prettier to make Resolvable a class that inherits from Promise,
|
||||||
|
// rather than an interface. This is possible in ES2016, however typescript
|
||||||
|
// produces broken code when targeting ES5 code.
|
||||||
|
// See https://github.com/Microsoft/TypeScript/issues/15202
|
||||||
|
// At the time of writing, the github issue is closed but the problem remains.
|
||||||
|
export interface Resolvable<T> extends Promise<T> {
|
||||||
|
resolve: (value?: T | PromiseLike<T>) => void;
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
reject: (reason?: any) => void;
|
||||||
|
}
|
||||||
|
export function createResolvable<T>(): Resolvable<T> {
|
||||||
|
let methods;
|
||||||
|
const promise = new Promise<T>((resolve, reject) => {
|
||||||
|
methods = { resolve, reject };
|
||||||
|
});
|
||||||
|
return Object.assign(promise, methods) as Resolvable<T>;
|
||||||
|
}
|
||||||
|
|
|
@ -67,6 +67,10 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/text-encoding@^0.0.32":
|
||||||
|
version "0.0.32"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/text-encoding/-/text-encoding-0.0.32.tgz#52289b320a406850b14f08f48b475ca021218048"
|
||||||
|
|
||||||
abbrev@1:
|
abbrev@1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||||
|
@ -3541,6 +3545,10 @@ tar@^4:
|
||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.1.2"
|
||||||
yallist "^3.0.2"
|
yallist "^3.0.2"
|
||||||
|
|
||||||
|
text-encoding@^0.6.4:
|
||||||
|
version "0.6.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
|
||||||
|
|
||||||
through2@^2.0.0, through2@~2.0.3:
|
through2@^2.0.0, through2@~2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
|
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
|
||||||
|
|
Loading…
Add table
Reference in a new issue