鍍金池/ 問答/HTML/ 多次請求因?yàn)榫W(wǎng)絡(luò)無法保證響應(yīng)順序,如何保證獲得所有響應(yīng)結(jié)果后生成的列表結(jié)果的順序

多次請求因?yàn)榫W(wǎng)絡(luò)無法保證響應(yīng)順序,如何保證獲得所有響應(yīng)結(jié)果后生成的列表結(jié)果的順序與請求順序一致?

看很多人都誤會成了一次性發(fā)送10次,補(bǔ)充為這樣:

假如用戶可以連續(xù)點(diǎn)擊按鈕,每次發(fā)1次請求,每次獲得響應(yīng)結(jié)果后會生成一個(gè)li,因?yàn)榫W(wǎng)絡(luò)狀態(tài)的影響后發(fā)出的請求可能先響應(yīng),假如點(diǎn)了10次,如何保證10個(gè)li的順序與請求的順序一致!

使用Promise.all的答案肯定都是不對的,因?yàn)槟銦o法預(yù)測用戶點(diǎn)擊按鈕的時(shí)機(jī),用戶可能一兩秒完成10次點(diǎn)擊,但也可能在10秒內(nèi)完成......肯定不能說我等用戶點(diǎn)完10次再使用Promise.all請求,何況實(shí)際情況不一定是10次。
這是我一次面試唯一沒答的很好的題目,所以印象比較深刻,我開始也答的Promise.all,被直接否定,后面我提到在請求報(bào)文中攜帶相關(guān)參數(shù),響應(yīng)中返回,本地做mapping關(guān)系來實(shí)現(xiàn),但面試官依然不是很滿意。后面經(jīng)我詢問后面試官只提到了思路,說了幾個(gè)詞記得不是很清楚,后續(xù)查資料猜測貌似是在請求響應(yīng)包含一個(gè)Request-Id字段,并使用 UUID 作為該值......沒實(shí)踐過不敢確定!

回答
編輯回答
黑與白

Promise.all可以保證順序但是得等到所有請求完畢才會觸發(fā)

function p1(time) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(time);
        }, time);
    })
}

Promise.all([p1(5000), p1(1000)]).then(function (res) {
    console.log(res);//[5000,1000]
});
2018年2月14日 06:14
編輯回答
孤客

es6的async函數(shù)可以解決這個(gè)問題,異步函數(shù)一旦遇到await就會先返回,等到異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)后面的語句。
http://es6.ruanyifeng.com/#do...

2018年9月17日 09:55
編輯回答
墻頭草

一,可以把請求回來的數(shù)據(jù)做一個(gè)標(biāo)識,然后把所有數(shù)據(jù) 都放到一個(gè)數(shù)組中,按標(biāo)識 排序。
二,使用Promise.all,接收的是一個(gè)數(shù)組,等到數(shù)組中的請求全部完成,就執(zhí)行Promise.all().then(values => {}),其中values就是一個(gè)數(shù)組,且排好序的

2017年3月3日 18:23
編輯回答
北城荒

萬無一失的方法是:
從第一個(gè)開始執(zhí)行,等第一個(gè)執(zhí)行結(jié)束后,再執(zhí)行第二個(gè),依次類推。
這種同步方法效率太低。
那么可以考慮Promise.all呢?
擔(dān)心promise.all中的所有task都是異步執(zhí)行的?
那么真正返回結(jié)果是依task列表順序返回,所以就它了。

2017年11月13日 11:00
編輯回答
任她鬧

不知道你說的是不是X-Request-ID,如果是,那我也不清楚怎么用這個(gè)header來區(qū)分不同的請求,除非是服務(wù)端配合。

我所想到的方法是使用一個(gè)自增的閉包變量來區(qū)分每個(gè)AJAX請求,這一點(diǎn)樓上很多人都想到了。

接下來,是如何在請求返回之后根據(jù)這個(gè)閉包變量的值在適當(dāng)?shù)奈恢貌迦?code><li>元素,以保證插入后的順序與點(diǎn)擊順序相同(而不是與請求返回的順序相同)。

我的方案是在請求的時(shí)候就append一個(gè)<li>元素,并設(shè)置為隱藏,同時(shí)給這個(gè)元素設(shè)置一個(gè)data,值為閉包變量的值。

當(dāng)請求返回之后,根據(jù)閉包變量找到對應(yīng)的<li>元素,插入內(nèi)容后將其顯示出來即可。類似于下面這樣:

var idx = 0;

$(btn).on("click", function() {
    let i = idx++;
    $(...).append('<li class="hide" data-idx="`${i}`"></li>');
    $.ajax({
        ...
        success: function(resp) {
            var li = $("li[data-idx='`${i}`']");
            ...
        };
    });
});
2018年9月1日 06:30
編輯回答
柚稚

以下代碼實(shí)現(xiàn)了:

  • 并發(fā)請求
  • 順序操作
  • 不需要等待全部請求完畢
  • 可以直接運(yùn)行
(()=>{
    Promise.allInOrder = (promises, thenForEach)=>{
        let sequence = Promise.resolve();
        
        promises.forEach(function(request){
            sequence = sequence.then(function(){
              return request.then(thenForEach);
          });
        });

    };
})();

// test
(()=>{
    let timeConsumingFunc = param=>new Promise(
            (resolve)=>{
                let timeout = Math.random() * 5000;
                console.log(`task ${param} will be resolved in ${timeout}ms`);
                setTimeout(()=>{
                    console.log(`${param} resolved`);
                    resolve(param+10);
                }, timeout);
            }
        );
    Promise
        .allInOrder(
            [timeConsumingFunc(1), timeConsumingFunc(2), timeConsumingFunc(3)],
            d=>{
                return new Promise(function(resolve) {
                  console.log(d);
                resolve();
              });
            }
        )
})();

這是我以前的一個(gè)提問的最終解決方法,也可以采用我采納的哪個(gè)答案,只是代碼會臃腫一些

補(bǔ)充:
針對你說的情況,可以間隔1s內(nèi)的鼠標(biāo)點(diǎn)擊作為一組請求用上面方法并發(fā)請求,間隔大于1s的同步繼發(fā)操作

2017年9月14日 18:41
編輯回答
真難過

其實(shí)如果后天和前臺能夠聯(lián)動,最好是請求時(shí)有一個(gè)標(biāo)號,然后返回時(shí)帶標(biāo)號,這樣肯定沒有問題,且無論多少返回都可以先展示,后期再依序調(diào)整。
其實(shí)如果更友好的,發(fā)一次請求,就在數(shù)組中填一個(gè)展位符信息,接收到一個(gè)就更新數(shù)組,并刷新展示。

2017年3月10日 04:07
編輯回答
妖妖

在請求的時(shí)候帶過去要顯示的li的順序;然后響應(yīng)回來順序;按順序排列吧

2018年6月6日 19:58
編輯回答
巴扎嘿

在請求里帶一個(gè)時(shí)間戳參數(shù),然后再原封不動把這個(gè)時(shí)間戳返回來,并根據(jù)時(shí)間進(jìn)行排序。

2018年4月21日 13:36
編輯回答
未命名

如果不是面試中,是實(shí)際項(xiàng)目中碰到類似問題,而且必須要保證返回順續(xù)的話,因?yàn)橛械臅r(shí)候第二次請求需要第一次的結(jié)果,我會采用當(dāng)用戶單擊按鈕以后,在請求沒有回來之前,按鈕變?yōu)榻脿顟B(tài),只有成功返回了,才可以進(jìn)行下一次請求,原因是你永遠(yuǎn)無法預(yù)測用戶是貓還是狗,所以最好的辦法就是不讓用戶有連續(xù)點(diǎn)擊的機(jī)會

2017年8月5日 02:29