2017-02-24 38 views
1

我被告知,Promise.all無法保證Promise.all能夠解決其承諾。但是,我不知道它不能按照Promise.all native docs的順序解決。用knex遷移承諾所有比賽條件

因此,下面的方法來knex遷移不應該工作,因爲messages有一個用戶表的引用。

然而,我從來沒有遇到過一次實例,在幾次遷移中,出現了競爭條件的錯誤。意思是,好像Promise.all根據索引位置決定。

所以,我的問題是:下面的代碼片段容易出現競爭狀態嗎?

return Promise.all([ 
     knex.schema.createTable('users', function(table) { 
     table.increments().primary(); 
     ... 
     }), 
     knex.schema.createTable('messages', function(table) { 
     table.increments().primary(); 
     table.bigInteger('user_id').unsigned().index() 
      .references('id').inTable('users'); 
     }), 

這是更好的方法嗎?

return Promise.all([ 
     knex.schema.createTable('users', function(table) { 
     table.increments().primary(); 
     ... 
     }), 
    ]).then(function() { 
     return Promise.all([ 
     knex.schema.createTable('messages', function(table) { 
      table.increments().primary(); 
      table.bigInteger('user_id').unsigned().index() 
      .references('id').inTable('users'); 
     }), 
     }); 
    }) 
+0

你不需要在你的第二個片段中使用'Promise.all'。 – 4castle

+0

假設這些是XHR調用,它們將在您退出函數範圍後立即啓動。這意味着XHR請求將同時運行。這可能是因爲你很幸運,請求的順序完全按照你想要的方式完成,但是如果'createTable('users')'需要在'createTable('messages')'之前執行',那麼你應該使用'.then()':'knex.schema.createTable('users',...)。then(()=> {knex.schema.createTable('messages')});'。 –

回答

1

既然你的依賴,在你需要已創建的第一個表,然後才能在第二個表引用它,你應該使用then三通。

Promise.all是否會看到它的承諾是否按索引順序解決,與Promise.all本身無關,但是以個體承諾作爲參數傳遞給它。雖然您可以期望JavaScript(而不是Promise.all)按順序評估參數列表,但您一般無法知道哪些承諾會首先解決。這由個人承諾決定,而不是由Promise.all決定。

就你的情況而言,這些個體承諾會做類似的事情,即創建一個表,並且你的數據庫引擎可能會按順序處理這些語句,而沒有併發性,你可能會在實踐中看到承諾總是按照你的順序列出它們,但依靠這種做法是不好的做法。

請注意,如果您只將一個承諾傳遞給Promise.all,則可以跳過該Promise.all並立即將then應用於該單個承諾。

+0

爲了簡潔起見,我只列出了我的許多承諾之一。謝謝你的解釋。 – Growler

0

我期待這段代碼在遷移文件中,默認情況下會創建隱式事務。所以,因爲查詢被髮送到同一個連接,它們將不會被並行解析。

在這種情況下,數據庫驅動程序(至少pg)實際上會緩存第二個查詢,並在將第二個查詢發送到數據庫服務器之前等待,直到解決第一個查詢。

唯一重要的是執行這兩個查詢並將其發送到pg-driver。

由於Promise.all不保證它會在knex.schema.createTable('messages',...)之前執行knex.schema.createTable('users',...)查詢,這意味着理論上有可能第一個代碼失敗。


TL;博士第一種方法是不容易出現競爭情況,但如果查詢在錯誤的順序執行時可能會失敗。第二種方法更好。