Как ждать обратного вызова URL перед отправкой HTTP-ответа в koa? - PullRequest
0 голосов
/ 06 июня 2018

У меня есть koa маршрутизатор, мне нужно вызвать API, где асинхронный возврат результата.Это означает, что я не могу получить свой результат немедленно, API вызовет мой callback url, когда все будет в порядке.Но теперь я должен использовать его как API синхронизации, что означает, что я должен ждать, пока не будет вызван URL обратного вызова.

Мой маршрутизатор выглядит так:

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",
    }
})

мой URL обратного вызова выглядит так:

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",
    };
})

Так как я могу заставить этот aync API действовать как sync API?

Ответы [ 4 ]

0 голосов
/ 28 февраля 2019

Просто передайте resol () другой функции.Например, вы можете сделать это следующим образом:

// 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",
    }; })

Я не тестировал его, но думаю, что он будет работать.

0 голосов
/ 07 февраля 2019

Хорошо, во-первых, это не должно быть для вас "целью". NodeJS работает лучше, чем ASync .

Однако, давайте предположим, что вы по-прежнему хотите его по какой-то причине, поэтому взгляните на пакет запроса синхронизации на npm (там есть огромное примечание, что вы не должны делать это в производстве.

Но, я надеюсь, вы имеете в виду, как упростить этот API (как в одном вызове, что-то вроде). Вам все еще нужно .next илиawait но в любом случае это будет один вызов.
Если это так, пожалуйста, прокомментируйте этот ответ, я могу написать вам возможный метод, который я использую сам.

0 голосов
/ 09 февраля 2019

Как насчет этого?

router.post("/voice", async (ctx, next) => {

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;

let response = null;
try{

  response = await new Promise((resolve,reject)=>{
      //call your ret_callback and when it finish call resolve(with response) and if it fails, just reject(with error);
  });
}catch(err){
  //errors
}

// get the answer in "ret_callback"
ctx.body = {
    result: "ret_callback result here",
}
});
0 голосов
/ 07 февраля 2019
function getMovieTitles(substr) {
        let movies = [];
        let fdata = (page, search, totalPage) => {
            let mpath = {
                host: "jsonmock.hackerrank.com",
                path: "/api/movies/search/?Title=" + search + "&page=" + page,
            };
            let raw = '';
            https.get(mpath, (res) => {
                res.on("data", (chunk) => {
                    raw += chunk;
                });

                res.on("end", () => {
                    tdata = JSON.parse(raw);
                    t = tdata;
                    totalPage(t);
                });
            });
        }

    fdata(1, substr, (t) => {
        i = 1;
        mdata = [];
        for (i = 1; i <= parseInt(t.total_pages); i++) {
                fdata(i, substr, (t) => {
                    t.data.forEach((v, index, arrs) => {
                        movies.push(v.Title);
                        if (index === arrs.length - 1) {
                            movies.sort();
                            if (parseInt(t.page) === parseInt(t.total_pages)) {
                                movies.forEach(v => {
                                    console.log(v)
                                })
                            }
                        }
                    });
                });
            }
        });


}

getMovieTitles("tom")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...