Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait for a url callback before send HTTP response in koa?

I have a koa router I need to call a api where will async return result. This means I cannot get my result immediately, the api will call my callback url when it's ok. But now I have to use it like a sync api which means I have to wait until the callback url is called.

My router like this:

router.post("/voice", async (ctx, next) => {
    // call a API here
    const params = {
        data: "xxx",
        callback_url: "http//myhost/ret_callback",
    };
    const req = new Request("http://xxx/api", {
        method: "POST",
        body: JSON.stringify(params),
    });
    const resp = await fetch(req);
    const data = await resp.json();

    // data here is not the result I want, this api just return a task id, this api will call my url back
    const taskid = data.taskid;

    // now I want to wait here until I got "ret_callback"

    // .... wait .... wait
    // "ret_callback" is called now
    // get the answer in "ret_callback"
    ctx.body = {
        result: "ret_callback result here",
    }
})

my callback url like this:

router.post("/ret_callback", async (ctx, next) => {
    const params = ctx.request.body;

    // taskid will tell me this answer to which question
    const taskid = params.taskid;
    // this is exactly what I want
    const result = params.text;

    ctx.body = {
        code: 0,
        message: "success",
    };
})

So how can I make this aync api act like a sync api?

like image 557
roger Avatar asked Jun 06 '18 16:06

roger


1 Answers

Just pass a resolve() to another function. For example, you can do it this way:

// use a map to save a lot of resolve()
const taskMap = new Map();

router.post("/voice", async (ctx, next) => {
    // call a API here
    const params = {
        data: "xxx",
        callback_url: "http//myhost/ret_callback",
    };
    const req = new Request("http://xxx/api", {
        method: "POST",
        body: JSON.stringify(params),
    });
    const resp = await fetch(req);
    const data = await resp.json();

    const result = await waitForCallback(data.taskid);

    ctx.body = {
        result,
    } })

const waitForCallback = (taskId) => {
    return new Promise((resolve, reject) => {
        const task = {};
        task.id = taskId;
        task.onComplete = (data) => {
            resolve(data);
        };
        task.onError = () => {
            reject();
        };
        taskMap.set(task.id, task);
    });
};

router.post("/ret_callback", async (ctx, next) => {
    const params = ctx.request.body;

    // taskid will tell me this answer to which question
    const taskid = params.taskid;
    // this is exactly what I want
    const result = params.text;

    // here you continue the waiting response
    taskMap.get(taskid).onComplete(result);
    // not forget to clean rubbish
    taskMap.delete(taskid);

    ctx.body = {
        code: 0,
        message: "success",
    }; })

I didn't test it but I think it will work.

like image 137
Renwei Liu Avatar answered Sep 18 '22 16:09

Renwei Liu