Xử lý job bất đồng bộ
Sáu trạng thái, bốn trạng thái kết thúc, và khi nào nên dừng.
SDK chưa phát hành. Nội dung dưới đây dùng HTTP thuần.
Gửi job trả về 202, nghĩa là đã nhận chứ chưa xong. Bạn theo dõi bằng cách hỏi trạng thái theo chu kỳ hoặc bằng webhook, và dừng khi job vào một trong bốn trạng thái kết thúc.
import { randomUUID } from "node:crypto";
const BASE = "https://<your-host>/api/v1/partners/addons/route-optimization";
const headers = { "api-key": process.env.ROUTE4GREEN_API_KEY! };
// A submit answers 202 Accepted: the job is recorded, not finished.
// Generate the idempotency key once and reuse it for every retry of THIS
// submit, so a network timeout cannot create a second job.
const idempotencyKey = randomUUID();
async function submitJob(body: unknown) {
const response = await fetch(`${BASE}/jobs`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": idempotencyKey, "content-type": "application/json" },
body: JSON.stringify(body),
});
const envelope = await response.json();
if (!envelope.success && String(envelope.error_code) === "IDEMPOTENCY_CONFLICT") {
// You reused the key with DIFFERENT content. The replay contract is:
// same key plus identical payload returns the original job; same key
// plus a different payload is refused with this 409. Do not retry the
// same call in a loop. Mint a new key for what is genuinely a new job.
throw new Error("Idempotency-Key was reused with a different payload");
}
if (!envelope.success) throw new Error(String(envelope.error_code));
// A replayed submit lands here too, returning the ORIGINAL job.
return envelope.data.id as string;
}
// The four terminal states, taken from the reference. QUEUED and
// PROCESSING are the only non-terminal ones.
const TERMINAL = new Set(["SUCCEEDED","PARTIAL","FAILED","CANCELLED"]);
async function waitForJob(jobId: string, deadlineMs = 30 * 60 * 1000) {
const startedAt = Date.now();
while (Date.now() - startedAt < deadlineMs) {
const response = await fetch(`${BASE}/jobs/${jobId}`, { headers });
const envelope = await response.json();
if (!envelope.success) throw new Error(String(envelope.error_code));
const status: string = envelope.data.status;
if (TERMINAL.has(status)) return status;
await new Promise((resolve) => setTimeout(resolve, 5_000));
}
throw new Error("Job did not reach a terminal state before the deadline");
}
const jobId = await submitJob({ external_reference: "your-own-id" });
const status = await waitForJob(jobId);
// Only SUCCEEDED and PARTIAL have a result to collect. FAILED and
// CANCELLED are terminal with nothing to fetch.
if (status === "SUCCEEDED" || status === "PARTIAL") {
const result = await fetch(`${BASE}/jobs/${jobId}/result`, { headers });
const envelope = await result.json();
if (envelope.success) use(envelope.data);
}Bảng trạng thái đầy đủ và ví dụ vòng lặp có giới hạn nằm trong phần tích hợp tối ưu tuyến.